Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Symfony Doctrine Event Converter Bundle Laravel Package

dualmedia/symfony-doctrine-event-converter-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Bundle

    composer require dualmedia/symfony-doctrine-event-converter-bundle
    

    Register in config/bundles.php:

    DualMedia\DoctrineEventConverterBundle\DoctrineEventConverterBundle::class => ['all' => true],
    
  2. Annotate Your Entity Extend IdentifiableInterface and mark with @ORM\Entity:

    use Doctrine\ORM\Mapping as ORM;
    use DualMedia\Common\Interface\IdentifiableInterface;
    
    #[ORM\Entity]
    class User implements IdentifiableInterface
    {
        #[ORM\Id, ORM\GeneratedValue, ORM\Column]
        private ?int $id = null;
    
        public function getId(): ?int { return $this->id; }
    }
    
  3. Create an Abstract Event Define a base event class (e.g., UserEvent):

    abstract class UserEvent extends AbstractDoctrineEvent
    {
        public function __construct(private User $user) {}
        public function getUser(): User { return $this->user; }
    }
    
  4. Trigger Events via Doctrine Use the bundle’s EventDispatcher to dispatch Symfony events from Doctrine operations:

    $entityManager->persist($user);
    $entityManager->flush(); // Triggers `UserEvent` automatically
    

Implementation Patterns

Core Workflow: Doctrine → Symfony Events

  1. Event Mapping Define a mapping between Doctrine lifecycle events (e.g., prePersist, postUpdate) and Symfony events:

    # config/packages/doctrine_event_converter.yaml
    doctrine_event_converter:
        mappings:
            App\Entity\User:
                prePersist: [App\Event\UserCreatedEvent]
                postUpdate: [App\Event\UserUpdatedEvent]
    
  2. Event Subscribers Subscribe to Symfony events in a subscriber service:

    class UserEventSubscriber implements EventSubscriberInterface
    {
        public static function getSubscribedEvents(): array
        {
            return [
                UserCreatedEvent::class => 'onUserCreated',
                UserUpdatedEvent::class => 'onUserUpdated',
            ];
        }
    
        public function onUserCreated(UserCreatedEvent $event): void
        {
            // Business logic (e.g., send welcome email)
        }
    }
    
  3. Conditional Event Dispatching Use EventDispatcher checks in your abstract event:

    abstract class UserEvent extends AbstractDoctrineEvent
    {
        public function __construct(private User $user)
        {
            if (!$this->shouldDispatch()) {
                return; // Skip if conditions fail (e.g., soft-deleted user)
            }
        }
    
        protected function shouldDispatch(): bool
        {
            return !$this->user->isDeleted();
        }
    }
    

Integration Tips

  • Doctrine Listeners as Fallback If the bundle doesn’t cover a use case, fall back to native Doctrine listeners:

    $eventManager->addEventListener(
        [User::class, 'prePersist'],
        [$this, 'handlePrePersist']
    );
    
  • Event Data Enrichment Extend events with metadata (e.g., timestamps, user context):

    class UserCreatedEvent extends UserEvent
    {
        public function __construct(User $user, private \DateTimeImmutable $createdAt) {}
    }
    
  • Testing Mock the EventDispatcher in tests:

    $dispatcher = $this->createMock(EventDispatcherInterface::class);
    $container->set('event_dispatcher', $dispatcher);
    

Gotchas and Tips

Pitfalls

  1. Missing IdentifiableInterface Forgetting to implement getId() will cause silent failures. Always verify:

    class User implements IdentifiableInterface { ... }
    
  2. Circular Dependencies Avoid injecting the EntityManager into events—use the bundle’s EventDispatcher instead:

    // ❌ Anti-pattern
    class UserEvent { public function __construct(EntityManager $em) {} }
    
    // ✅ Preferred
    class UserEvent { public function __construct(User $user) {} }
    
  3. Event Ordering Symfony events fire after Doctrine lifecycle callbacks. For pre-hooks, use pre* events:

    mappings:
        App\Entity\User:
            prePersist: [App\Event\ValidateUserEvent] # Runs before persist
    
  4. Performance Overhead Heavy event logic in post* hooks may slow down bulk operations. Offload to async tasks (e.g., Symfony Messenger).

Debugging

  • Enable Debug Mode Set DUALMEDIA_DEBUG: true in .env to log dispatched events:

    DUALMEDIA_DEBUG=1
    
  • Check Event Dispatching Verify events are fired via Doctrine’s EventManager logs:

    bin/console debug:event-dispatcher
    

Extension Points

  1. Custom Event Converters Override the default converter for complex entities:

    class CustomUserConverter implements EventConverterInterface
    {
        public function convert(UnitOfWork $uow, EntityChangeSet $changeSet): array
        {
            return [new UserUpdatedEvent($changeSet->getEntity())];
        }
    }
    

    Register in services.yaml:

    services:
        App\Event\Converter\CustomUserConverter:
            tags: [doctrine_event_converter.converter]
    
  2. Dynamic Event Mapping Use a service to generate mappings at runtime (e.g., based on entity traits):

    class DynamicMappingService
    {
        public function getMappings(): array
        {
            return [
                User::class => [
                    'prePersist' => [UserCreatedEvent::class],
                ],
            ];
        }
    }
    
  3. Event Validation Add constraints to events (e.g., validate User before dispatching):

    class UserCreatedEvent extends UserEvent
    {
        public function __construct(User $user)
        {
            if (!$user->isValid()) {
                throw new \RuntimeException('Invalid user data');
            }
        }
    }
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
comsave/common
alecsammon/php-raml-parser
chrome-php/wrench
lendable/composer-license-checker
typhoon/reflection
mesilov/moneyphp-percentage
mike42/gfx-php
bookdown/themes
aura/view
aura/html
aura/cli
povils/phpmnd
nayjest/manipulator
omnipay/tests
psr-mock/http-message-implementation
psr-mock/http-factory-implementation
psr-mock/http-client-implementation
voku/email-check
voku/urlify
rtheunissen/guzzle-log-middleware