Events ====== Doctrine features a lightweight event system that is part of the Common package. The Event System ---------------- The event system is controlled by the ``EventManager``. It is the central point of Doctrine's event listener system. Listeners are registered on the manager and events are dispatched through the manager. .. code-block:: php addEventListener(array(self::preFoo, self::postFoo), $this); } public function preFoo(EventArgs $e) { $this->preFooInvoked = true; } public function postFoo(EventArgs $e) { $this->postFooInvoked = true; } } // Create a new instance $test = new EventTest($evm); Events can be dispatched by using the ``dispatchEvent()`` method. .. code-block:: php dispatchEvent(EventTest::preFoo); $evm->dispatchEvent(EventTest::postFoo); You can easily remove a listener with the ``removeEventListener()`` method. .. code-block:: php removeEventListener(array(self::preFoo, self::postFoo), $this); The Doctrine event system also has a simple concept of event subscribers. We can define a simple ``TestEventSubscriber`` class which implements the ``\Doctrine\Common\EventSubscriber`` interface and implements a ``getSubscribedEvents()`` method which returns an array of events it should be subscribed to. .. code-block:: php preFooInvoked = true; } public function getSubscribedEvents() { return array(self::preFoo); } } $eventSubscriber = new TestEventSubscriber(); $evm->addEventSubscriber($eventSubscriber); Now when you dispatch an event any event subscribers will be notified for that event. .. code-block:: php dispatchEvent(TestEventSubscriber::preFoo); Now test the ``$eventSubscriber`` instance to see if the ``preFoo()`` method was invoked. .. code-block:: php preFooInvoked) { echo 'pre foo invoked!'; } .. _lifecycle_events: Lifecycle Events ---------------- The DocumentManager and UnitOfWork trigger several events during the life-time of their registered documents. - preRemove - The preRemove event occurs for a given document before the respective DocumentManager remove operation for that document is executed. - postRemove - The postRemove event occurs for a document after the document has been removed. It will be invoked after the database delete operations. - prePersist - The prePersist event occurs for a given document before the respective DocumentManager persist operation for that document is executed. - postPersist - The postPersist event occurs for a document after the document has been made persistent. It will be invoked after the database insert operations. Generated primary key values are available in the postPersist event. - preUpdate - The preUpdate event occurs before the database update operations to document data. - postUpdate - The postUpdate event occurs after the database update operations to document data. - preLoad - The preLoad event occurs for a document before the document has been loaded into the current DocumentManager from the database or after the refresh operation has been applied to it. - postLoad - The postLoad event occurs for a document after the document has been loaded into the current DocumentManager from the database or after the refresh operation has been applied to it. - loadClassMetadata - The loadClassMetadata event occurs after the mapping metadata for a class has been loaded from a mapping source (annotations/xml/yaml). - preFlush - The preFlush event occurs before the change-sets of all managed documents are computed. This both a lifecycle call back and and listener. - postFlush - The postFlush event occurs after the change-sets of all managed documents are computed. - onFlush - The onFlush event occurs after the change-sets of all managed documents are computed. This event is not a lifecycle callback. - onClear - The onClear event occurs after the UnitOfWork has had its state cleared. - documentNotFound - The documentNotFound event occurs when a proxy object could not be initialized. This event is not a lifecycle callback. - postCollectionLoad - The postCollectionLoad event occurs just after collection has been initialized (loaded) and before new elements are re-added to it. You can access the Event constants from the ``Events`` class in the ODM package. .. code-block:: php createdAt = date('Y-m-d H:i:s'); } /** @PrePersist */ public function doOtherStuffOnPrePersist(\Doctrine\ODM\MongoDB\Event\LifecycleEventArgs $eventArgs) { $this->value = 'changed from prePersist callback!'; } /** @PostPersist */ public function doStuffOnPostPersist(\Doctrine\ODM\MongoDB\Event\LifecycleEventArgs $eventArgs) { $this->value = 'changed from postPersist callback!'; } /** @PreLoad */ public function doStuffOnPreLoad(\Doctrine\ODM\MongoDB\Event\PreLoadEventArgs $eventArgs) { $data =& $eventArgs->getData(); $data['value'] = 'changed from preLoad callback'; } /** @PostLoad */ public function doStuffOnPostLoad(\Doctrine\ODM\MongoDB\Event\LifecycleEventArgs $eventArgs) { $this->value = 'changed from postLoad callback!'; } /** @PreUpdate */ public function doStuffOnPreUpdate(\Doctrine\ODM\MongoDB\Event\PreUpdateEventArgs $eventArgs) { $this->value = 'changed from preUpdate callback!'; } /** @PreFlush */ public function preFlush(\Doctrine\ODM\MongoDB\Event\PreFlushEventArgs $eventArgs) { $this->value = 'changed from preFlush callback!'; } } Note that when using annotations you have to apply the @HasLifecycleCallbacks marker annotation on the document class. Listening to Lifecycle Events ----------------------------- Lifecycle event listeners are much more powerful than the simple lifecycle callbacks that are defined on the document classes. They allow to implement re-usable behaviours between different document classes, yet require much more detailed knowledge about the inner workings of the DocumentManager and UnitOfWork. Please read the *Implementing Event Listeners* section carefully if you are trying to write your own listener. To register an event listener you have to hook it into the EventManager that is passed to the DocumentManager factory: .. code-block:: php addEventListener(array(Events::preUpdate), new MyEventListener()); $eventManager->addEventSubscriber(new MyEventSubscriber()); $documentManager = DocumentManager::create($mongo, $config, $eventManager); You can also retrieve the event manager instance after the DocumentManager was created: .. code-block:: php getEventManager()->addEventListener(array(Events::preUpdate), new MyEventListener()); $documentManager->getEventManager()->addEventSubscriber(new MyEventSubscriber()); Implementing Event Listeners ---------------------------- This section explains what is and what is not allowed during specific lifecycle events of the UnitOfWork. Although you get passed the DocumentManager in all of these events, you have to follow this restrictions very carefully since operations in the wrong event may produce lots of different errors, such as inconsistent data and lost updates/persists/removes. prePersist ~~~~~~~~~~ Listen to the ``prePersist`` event: .. code-block:: php getEventManager(); $evm->addEventListener(Events::prePersist, $test); Define the ``EventTest`` class: .. code-block:: php getDocument(); $document->setSomething(); } } preLoad ~~~~~~~ .. code-block:: php getEventManager(); $evm->addEventListener(Events::preLoad, $test); Define the ``EventTest`` class with a ``preLoad()`` method: .. code-block:: php getData(); // do something } } postLoad ~~~~~~~~ .. code-block:: php getEventManager(); $evm->addEventListener(Events::postLoad, $test); Define the ``EventTest`` class with a ``postLoad()`` method: .. code-block:: php getDocument(); // do something } } preRemove ~~~~~~~~~ .. code-block:: php getEventManager(); $evm->addEventListener(Events::preRemove, $test); Define the ``EventTest`` class with a ``preRemove()`` method: .. code-block:: php getDocument(); // do something } } preFlush ~~~~~~~~ .. code-block:: php getEventManager(); $evm->addEventListener(Events::preFlush, $test); Define the ``EventTest`` class with a ``preFlush()`` method: .. code-block:: php getDocumentManager(); $uow = $dm->getUnitOfWork(); // do something } } onFlush ~~~~~~~ .. code-block:: php getEventManager(); $evm->addEventListener(Events::onFlush, $test); Define the ``EventTest`` class with a ``onFlush()`` method: .. code-block:: php getDocumentManager(); $uow = $dm->getUnitOfWork(); // do something } } postFlush ~~~~~~~~~ .. code-block:: php getEventManager(); $evm->addEventListener(Events::postFlush, $test); Define the ``EventTest`` class with a ``postFlush()`` method: .. code-block:: php getDocumentManager(); $uow = $dm->getUnitOfWork(); // do something } } preUpdate ~~~~~~~~~ .. code-block:: php getEventManager(); $evm->addEventListener(Events::preUpdate, $test); Define the ``EventTest`` class with a ``preUpdate()`` method: .. code-block:: php getDocument(); $document->setSomething(); $dm = $eventArgs->getDocumentManager(); $class = $dm->getClassMetadata(get_class($document)); $dm->getUnitOfWork()->recomputeSingleDocumentChangeSet($class, $document); } } .. note:: If you modify a document in the preUpdate event you must call ``recomputeSingleDocumentChangeSet`` for the modified document in order for the changes to be persisted. onClear ~~~~~~~ .. code-block:: php getEventManager(); $evm->addEventListener(Events::onClear, $test); Define the ``EventTest`` class with a ``onClear()`` method: .. code-block:: php getDocumentClass(); $dm = $eventArgs->getDocumentManager(); $uow = $dm->getUnitOfWork(); // Check if event clears all documents. if ($eventArgs->clearsAllDocuments()) { // do something } // do something } } documentNotFound ~~~~~~~~~~~~~~~~ .. code-block:: php getEventManager(); $evm->addEventListener(Events::documentNotFound, $test); Define the ``EventTest`` class with a ``documentNotFound()`` method: .. code-block:: php getObject(); $identifier = $eventArgs->getIdentifier(); // do something // To prevent the documentNotFound exception from being thrown, call the disableException() method: $eventArgs->disableException(); } } postUpdate, postRemove, postPersist ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: php getEventManager(); $evm->addEventListener(Events::postUpdate, $test); $evm->addEventListener(Events::postRemove, $test); $evm->addEventListener(Events::postPersist, $test); Define the ``EventTest`` class with a ``postUpdate()``, ``postRemove()`` and ``postPersist()`` method: .. code-block:: php getEventManager(); $evm->addEventListener(Events::postCollectionLoad, $test); Define the ``EventTest`` class with a ``postCollectionLoad()`` method: .. code-block:: php getCollection(); if ($collection instanceof \Malarzm\Collections\DiffableCollection) { $collection->snapshot(); } } } Load ClassMetadata Event ------------------------ When the mapping information for a document is read, it is populated in to a ``ClassMetadata`` instance. You can hook in to this process and manipulate the instance with the ``loadClassMetadata`` event: .. code-block:: php getMetadataFactory(); $evm = $dm->getEventManager(); $evm->addEventListener(Events::loadClassMetadata, $test); class EventTest { public function loadClassMetadata(\Doctrine\ODM\MongoDB\Event\LoadClassMetadataEventArgs $eventArgs) { $classMetadata = $eventArgs->getClassMetadata(); $fieldMapping = array( 'fieldName' => 'about', 'type' => 'string' ); $classMetadata->mapField($fieldMapping); } }