PHP code example of ericgansa / ghost-trees-bundle

1. Go to this page and download the library: Download ericgansa/ghost-trees-bundle library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

ericgansa / ghost-trees-bundle example snippets


EricGansa\GhostTreesBundle\GhostTreesBundle::class => ['all' => true],

use EricGansa\GhostTreesBundle\Attribute\GhostField;
use EricGansa\GhostTreesBundle\Attribute\RequiredOnRoot;
use EricGansa\GhostTreesBundle\Contract\GhostableInterface;
use EricGansa\GhostTreesBundle\Trait\GhostNodeTrait;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class Trajet implements GhostableInterface
{
    use GhostNodeTrait;   // fournit getParent(), setParent(), isGhost(),
                          // resolve(), incarnate(), reset(), createGhostOf()

    #[ORM\Column(nullable: true)]
    #[GhostField]                // marque la propriété pour la résolution dynamique
    #[RequiredOnRoot]            // obligatoire sur les racines, silencieux sur les fantômes
    private ?string $lieuDepart = null;

    // Redéclaration UNIQUEMENT pour le mapping Doctrine (targetEntity requis).
    #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')]
    #[ORM\JoinColumn(nullable: true, onDelete: 'CASCADE')]
    protected ?GhostableInterface $parent = null;

    public function getLieuDepart(): ?string
    {
        return $this->resolve($this->lieuDepart, 'getLieuDepart');
        //             ↑ retourne la valeur locale si non-null, sinon remonte au parent
    }
}

// Incarnation sans service (autonome)
$ghost->incarnate();
// → $ghost est maintenant une racine indépendante avec toutes ses valeurs matérialisées

// Retour à la transparence (annule les surcharges)
$ghost->reset();
// → $ghost lit de nouveau toutes ses valeurs depuis le parent

// Fabrique un fantôme
$copy = Trajet::createGhostOf($original);
// → $copy->getParent() === $original, tous les champs à null (lecture transparente)

use EricGansa\GhostTreesBundle\Contract\GhostResolverInterface;

public function __construct(private GhostResolverInterface $resolver) {}

public function attachToParent(Trajet $child, Trajet $parent): void
{
    // Lève GhostDepthExceededException ou GhostCycleException si invalide.
    $this->resolver->assertValidParent($child, $parent);
    $child->setParent($parent);
}

use EricGansa\GhostTreesBundle\Contract\GhostInspectorInterface;

public function showResolution(Trajet $ghost, GhostInspectorInterface $inspector): array
{
    return $inspector->debugResolution($ghost);
    // [
    //   'lieuDepart'  => ['value' => 'Paris',     'source' => 'inherited', 'depth' => 1],
    //   'lieuArrivee' => ['value' => 'Marseille', 'source' => 'local',     'depth' => 0],
    // ]
}

use EricGansa\GhostTreesBundle\Contract\GhostIncarnatorInterface;

public function incarnate(Trajet $ghost, GhostIncarnatorInterface $incarnator, EntityManagerInterface $em): void
{
    $em->wrapInTransaction(function () use ($incarnator, $ghost) {
        $incarnator->incarnate($ghost);  // émet GhostIncarnatedEvent
    });
}

use EricGansa\GhostTreesBundle\Event\GhostIncarnatedEvent;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;

#[AsEventListener(event: GhostIncarnatedEvent::class)]
final class IncarnationAuditor
{
    public function __construct(private LoggerInterface $logger) {}

    public function __invoke(GhostIncarnatedEvent $event): void
    {
        $this->logger->info('Ghost incarnated', [
            'entity' => $event->entity::class . '#' . $event->entity->getId(),
            'previousParent' => $event->previousParent?->getId(),
        ]);
    }
}

#[AsEventListener(event: GhostIncarnatedEvent::class)]
final class NotifyAuthorOnIncarnation
{
    public function __invoke(GhostIncarnatedEvent $event): void
    {
        $previous = $event->previousParent;
        if ($previous instanceof Recipe && $previous->getAuthor()) {
            $this->mailer->send(new RecipeIncarnatedMail($previous->getAuthor(), $event->entity));
        }
    }
}
bash
php bin/console debug:ghosts "App\Entity\Trajet" 42
php bin/console ghosts:incarnate "App\Entity\Trajet" 42