dmytrof/doctrine-modification-events-bundle
Installation:
composer require dmytrof/doctrine-modification-events-bundle
Enable the bundle in config/bundles.php:
Dmytrof\DoctrineModificationEventsBundle\DmytrofDoctrineModificationEventsBundle::class => ['all' => true],
First Use Case:
@ModificationEvent to automatically dispatch events when fields change.
use Dmytrof\DoctrineModificationEventsBundle\Annotation\ModificationEvent;
/**
* @ModificationEvent("user.updated", fields={"email", "status"})
*/
class User {}
namespace App\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Dmytrof\DoctrineModificationEventsBundle\Event\ModificationEvent;
class UserUpdateListener implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
'user.updated' => 'onUserUpdated',
];
}
public function onUserUpdated(ModificationEvent $event): void
{
$entity = $event->getEntity();
$changes = $event->getChanges();
// Handle changes (e.g., log, notify, audit)
}
}
Where to Look First:
Annotation and Event classes in the bundle’s src directory for available configurations.@ModificationEvent annotation for defining event triggers.Tracking Field-Specific Changes:
Use the fields attribute in @ModificationEvent to narrow event triggers to specific fields:
/**
* @ModificationEvent("order.status_changed", fields={"status"})
*/
class Order {}
public function onOrderStatusChanged(ModificationEvent $event): void
{
$oldStatus = $event->getOldValue('status');
$newStatus = $event->getNewValue('status');
// Logic for status transitions (e.g., send notifications)
}
Bulk Entity Updates:
For Doctrine\ORM\QueryBuilder or Repository::createQueryBuilder(), ensure the bundle’s event listener is registered globally (via bundles.php). Events will trigger automatically for UPDATE operations.
Conditional Event Dispatching:
Combine with Symfony’s EventDispatcher to add logic:
public function onUserUpdated(ModificationEvent $event): void
{
if ($event->hasChange('status') && $event->getNewValue('status') === 'active') {
$this->notifyAdmin($event->getEntity());
}
}
Integration with Doctrine Lifecycle Callbacks:
Use @PreUpdate or @PostUpdate alongside @ModificationEvent for hybrid workflows:
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\PreUpdate
* @ModificationEvent("user.pre_update")
*/
class User {}
Dynamic Event Names: Generate event names dynamically based on entity metadata:
$eventName = sprintf('entity.%s.%s', $entityClass, 'updated');
$this->eventDispatcher->dispatch($eventName, new ModificationEvent($entity, $changes));
app.user.updated) to avoid collisions.public function testUserEmailUpdateEvent()
{
$user = new User();
$user->setEmail('new@example.com');
$this->entityManager->persist($user);
$this->entityManager->flush();
$this->assertEventDispatched('user.updated');
}
Event Not Triggering:
bundles.php or misconfiguring the annotation.doctrine:schema:validate to check).Incorrect Field Changes:
fields attribute in @ModificationEvent is case-sensitive or misspelled.Circular Dependencies:
UserListener updating User in onUserUpdated).Doctrine Proxy Issues:
entityManager->refresh() if needed.Symfony Cache:
php bin/console cache:clear
public function onUserUpdated(ModificationEvent $event): void
{
$this->logger->info('Event triggered', ['event' => $event->getName(), 'changes' => $event->getChanges()]);
}
EventDispatcher debug command to list subscribed events:
php bin/console debug:event-dispatcher
UnitOfWork to debug changes:
$uow = $this->entityManager->getUnitOfWork();
$changes = $uow->getEntityChangeSet($entity);
Custom Event Classes:
Extend Dmytrof\DoctrineModificationEventsBundle\Event\ModificationEvent to add custom data:
class CustomModificationEvent extends ModificationEvent
{
public function __construct(object $entity, array $changes, private string $customData) {}
public function getCustomData(): string { return $this->customData; }
}
Update annotations to use the new event name.
Dynamic Field Mapping:
Override the bundle’s ModificationEventListener to dynamically resolve fields:
public function onFlush(): void
{
$entity = $this->uow->getEntityChangeSet($entity);
if ($entity && $this->isEventTriggered($entity)) {
$this->dispatchEvent($entity, $entity);
}
}
Batch Processing: For bulk updates, disable event dispatching temporarily:
$this->entityManager->getEventManager()->removeEventListeners('user.updated');
// Perform bulk updates
$this->entityManager->flush();
$this->entityManager->getEventManager()->addEventListener('user.updated', [$listener, 'onUserUpdated']);
API Versioning:
Use events to trigger actions for specific API versions (e.g., user.v1.updated). Combine with Symfony’s RequestContext or custom headers.
How can I help you explore Laravel packages today?