Installation:
composer require andreas-glaser/dc-event-bundle ^1
Ensure your project uses Symfony 3.4+ and PHP 7.2+.
Enable the Bundle:
Add to config/bundles.php:
return [
// ...
AndreasGlaser\DCEventBundle\DCEventBundle::class => ['all' => true],
];
First Use Case:
@DCEntityEventHandler to attach a custom event handler.Article entity before/after persistence.src/Entity/ for entities needing lifecycle hooks.EEH (Entity Event Handler) classes in src/EEH/ or create a new directory.prePersist, postPersist, preUpdate, etc., in your handler classes.Change Tracking:
Use ChangeSetHelper in preUpdate to inspect field changes:
public function preUpdate(ChangeSetHelper $changeSet) {
if ($changeSet->isChanged('subject')) {
$this->entity->setSubject(strtoupper($changeSet->getOldValue('subject')));
}
}
Conditional Logic:
Implement prePersist to validate or transform data before saving:
public function prePersist() {
if (empty($this->entity->getBody())) {
throw new \RuntimeException('Article body cannot be empty.');
}
}
Post-Save Actions:
Use postPersist or postUpdate to trigger side effects (e.g., logging, notifications):
public function postPersist() {
$this->logAction('article_created', $this->entity->getId());
}
Bulk Operations:
Handle multiple flushes by clearing state in postUpdate/postRemove:
public function postUpdate() {
$this->entity = null; // Reset entity reference
}
Dependency Injection:
Inject services into handlers via constructor (extend DCEntityEventHandlerBase):
public function __construct(private LoggerInterface $logger) {}
Testing:
Mock ChangeSetHelper in unit tests to verify logic:
$changeSet = $this->createMock(ChangeSetHelper::class);
$changeSet->method('isChanged')->willReturn(true);
$handler->preUpdate($changeSet);
Performance:
Avoid heavy operations in preFlush (e.g., API calls). Use async tasks for post-save actions.
Multiple Flushes:
EntityManager::flush() is called repeatedly (e.g., in loops).postUpdate/postRemove or use EntityManager::clear().ChangeSet Staleness:
ChangeSetHelper reflects state before the current operation. Use getOldValue()/getNewValue() carefully.public function preUpdate(ChangeSetHelper $changeSet) {
error_log($changeSet->getChanges());
}
Annotation Caching:
@DCEntityEventHandler changes immediately.php bin/console cache:clear
Circular Dependencies:
EntityManager::getUnitOfWork()->propertyChanged() to check if an entity is already being processed.Enable Doctrine Events:
Add to config/packages/dev/doctrine.yaml:
doctrine:
orm:
event_listeners:
logger:
class: Doctrine\ORM\Debug\Logger\ORMLogger
tag: ['doctrine.event_listener']
Log Handlers:
Override DCEntityEventHandlerBase to log lifecycle calls:
public function __construct() {
parent::__construct();
$this->logger = new \Monolog\Logger('dc_event');
}
Custom Annotations:
Extend the annotation system to support additional metadata (e.g., @DCEntityEventHandler(priority=10)).
Global Handlers: Register handlers dynamically via services (e.g., for entities without annotations):
services:
App\EEH\GlobalArticleEEH:
tags:
- { name: 'dc_event.handler', entity: 'App\Entity\Article' }
Async Processing:
Offload post-save tasks to a queue (e.g., Symfony Messenger) in postPersist:
public function postPersist() {
$this->messageBus->dispatch(new NotifyArticleCreated($this->entity));
}
Bundle Priority:
The bundle hooks into preFlush, which runs before prePersist/preUpdate. Ensure your logic accounts for this order.
Entity Manager Scope:
Handlers are tied to the EntityManager instance. For multi-EM setups, register handlers per manager.
How can I help you explore Laravel packages today?