718 lines
18 KiB
ReStructuredText
718 lines
18 KiB
ReStructuredText
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
|
|
|
|
<?php
|
|
|
|
$evm = new EventManager();
|
|
|
|
Now we can add some event listeners to the ``$evm``. Let's create a
|
|
``EventTest`` class to play around with.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
class EventTest
|
|
{
|
|
const preFoo = 'preFoo';
|
|
const postFoo = 'postFoo';
|
|
|
|
private $_evm;
|
|
|
|
public $preFooInvoked = false;
|
|
public $postFooInvoked = false;
|
|
|
|
public function __construct($evm)
|
|
{
|
|
$evm->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
|
|
|
|
<?php
|
|
|
|
$evm->dispatchEvent(EventTest::preFoo);
|
|
$evm->dispatchEvent(EventTest::postFoo);
|
|
|
|
You can easily remove a listener with the ``removeEventListener()``
|
|
method.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
$evm->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
|
|
|
|
<?php
|
|
|
|
class TestEventSubscriber implements \Doctrine\Common\EventSubscriber
|
|
{
|
|
const preFoo = 'preFoo';
|
|
|
|
public $preFooInvoked = false;
|
|
|
|
public function preFoo()
|
|
{
|
|
$this->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
|
|
|
|
<?php
|
|
|
|
$evm->dispatchEvent(TestEventSubscriber::preFoo);
|
|
|
|
Now test the ``$eventSubscriber`` instance to see if the
|
|
``preFoo()`` method was invoked.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
if ($eventSubscriber->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
|
|
|
|
<?php
|
|
|
|
use Doctrine\ODM\MongoDB\Events;
|
|
|
|
echo Events::preUpdate;
|
|
|
|
These can be hooked into by two different types of event
|
|
listeners:
|
|
|
|
-
|
|
Lifecycle Callbacks are methods on the document classes that are
|
|
called when the event is triggered. They receive instances
|
|
of ``Doctrine\ODM\MongoDB\Event\LifecycleEventArgs`` (see relevant
|
|
examples below) as arguments and are specifically designed to allow
|
|
changes inside the document classes state.
|
|
-
|
|
Lifecycle Event Listeners are classes with specific callback
|
|
methods that receives some kind of ``EventArgs`` instance which
|
|
give access to the document, DocumentManager or other relevant
|
|
data.
|
|
|
|
.. note::
|
|
|
|
All Lifecycle events that happen during the ``flush()`` of
|
|
a DocumentManager have very specific constraints on the allowed
|
|
operations that can be executed. Please read the
|
|
*Implementing Event Listeners* section very carefully to understand
|
|
which operations are allowed in which lifecycle event.
|
|
|
|
Lifecycle Callbacks
|
|
-------------------
|
|
|
|
A lifecycle event is a regular event with the additional feature of
|
|
providing a mechanism to register direct callbacks inside the
|
|
corresponding document classes that are executed when the lifecycle
|
|
event occurs.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
/** @Document @HasLifecycleCallbacks */
|
|
class User
|
|
{
|
|
// ...
|
|
|
|
/**
|
|
* @Field
|
|
*/
|
|
public $value;
|
|
|
|
/** @Field */
|
|
private $createdAt;
|
|
|
|
/** @PrePersist */
|
|
public function doStuffOnPrePersist(\Doctrine\ODM\MongoDB\Event\LifecycleEventArgs $eventArgs)
|
|
{
|
|
$this->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
|
|
|
|
<?php
|
|
|
|
$eventManager = new EventManager();
|
|
$eventManager->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
|
|
|
|
<?php
|
|
|
|
$documentManager->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
|
|
|
|
<?php
|
|
|
|
$test = new EventTest();
|
|
$evm = $dm->getEventManager();
|
|
$evm->addEventListener(Events::prePersist, $test);
|
|
|
|
Define the ``EventTest`` class:
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
class EventTest
|
|
{
|
|
public function prePersist(\Doctrine\ODM\MongoDB\Event\LifecycleEventArgs $eventArgs)
|
|
{
|
|
$document = $eventArgs->getDocument();
|
|
$document->setSomething();
|
|
}
|
|
}
|
|
|
|
preLoad
|
|
~~~~~~~
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
$test = new EventTest();
|
|
$evm = $dm->getEventManager();
|
|
$evm->addEventListener(Events::preLoad, $test);
|
|
|
|
Define the ``EventTest`` class with a ``preLoad()`` method:
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
class EventTest
|
|
{
|
|
public function preLoad(\Doctrine\ODM\MongoDB\Event\PreLoadEventArgs $eventArgs)
|
|
{
|
|
$data =& $eventArgs->getData();
|
|
// do something
|
|
}
|
|
}
|
|
|
|
postLoad
|
|
~~~~~~~~
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
$test = new EventTest();
|
|
$evm = $dm->getEventManager();
|
|
$evm->addEventListener(Events::postLoad, $test);
|
|
|
|
Define the ``EventTest`` class with a ``postLoad()`` method:
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
class EventTest
|
|
{
|
|
public function postLoad(\Doctrine\ODM\MongoDB\Event\LifecycleEventArgs $eventArgs)
|
|
{
|
|
$document = $eventArgs->getDocument();
|
|
// do something
|
|
}
|
|
}
|
|
|
|
preRemove
|
|
~~~~~~~~~
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
$test = new EventTest();
|
|
$evm = $dm->getEventManager();
|
|
$evm->addEventListener(Events::preRemove, $test);
|
|
|
|
Define the ``EventTest`` class with a ``preRemove()`` method:
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
class EventTest
|
|
{
|
|
public function preRemove(\Doctrine\ODM\MongoDB\Event\LifecycleEventArgs $eventArgs)
|
|
{
|
|
$document = $eventArgs->getDocument();
|
|
// do something
|
|
}
|
|
}
|
|
|
|
preFlush
|
|
~~~~~~~~
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
$test = new EventTest();
|
|
$evm = $dm->getEventManager();
|
|
$evm->addEventListener(Events::preFlush, $test);
|
|
|
|
Define the ``EventTest`` class with a ``preFlush()`` method:
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
class EventTest
|
|
{
|
|
public function preFlush(\Doctrine\ODM\MongoDB\Event\PreFlushEventArgs $eventArgs)
|
|
{
|
|
$dm = $eventArgs->getDocumentManager();
|
|
$uow = $dm->getUnitOfWork();
|
|
// do something
|
|
}
|
|
}
|
|
|
|
onFlush
|
|
~~~~~~~
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
$test = new EventTest();
|
|
$evm = $dm->getEventManager();
|
|
$evm->addEventListener(Events::onFlush, $test);
|
|
|
|
Define the ``EventTest`` class with a ``onFlush()`` method:
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
class EventTest
|
|
{
|
|
public function onFlush(\Doctrine\ODM\MongoDB\Event\OnFlushEventArgs $eventArgs)
|
|
{
|
|
$dm = $eventArgs->getDocumentManager();
|
|
$uow = $dm->getUnitOfWork();
|
|
// do something
|
|
}
|
|
}
|
|
|
|
postFlush
|
|
~~~~~~~~~
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
$test = new EventTest();
|
|
$evm = $dm->getEventManager();
|
|
$evm->addEventListener(Events::postFlush, $test);
|
|
|
|
Define the ``EventTest`` class with a ``postFlush()`` method:
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
class EventTest
|
|
{
|
|
public function postFlush(\Doctrine\ODM\MongoDB\Event\PostFlushEventArgs $eventArgs)
|
|
{
|
|
$dm = $eventArgs->getDocumentManager();
|
|
$uow = $dm->getUnitOfWork();
|
|
// do something
|
|
}
|
|
}
|
|
|
|
preUpdate
|
|
~~~~~~~~~
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
$test = new EventTest();
|
|
$evm = $dm->getEventManager();
|
|
$evm->addEventListener(Events::preUpdate, $test);
|
|
|
|
Define the ``EventTest`` class with a ``preUpdate()`` method:
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
class EventTest
|
|
{
|
|
public function preUpdate(\Doctrine\ODM\MongoDB\Event\LifecycleEventArgs $eventArgs)
|
|
{
|
|
$document = $eventArgs->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
|
|
|
|
<?php
|
|
|
|
$test = new EventTest();
|
|
$evm = $dm->getEventManager();
|
|
$evm->addEventListener(Events::onClear, $test);
|
|
|
|
Define the ``EventTest`` class with a ``onClear()`` method:
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
class EventTest
|
|
{
|
|
public function onClear(\Doctrine\ODM\MongoDB\Event\OnClearEventArgs $eventArgs)
|
|
{
|
|
$class = $eventArgs->getDocumentClass();
|
|
$dm = $eventArgs->getDocumentManager();
|
|
$uow = $dm->getUnitOfWork();
|
|
|
|
// Check if event clears all documents.
|
|
if ($eventArgs->clearsAllDocuments()) {
|
|
// do something
|
|
}
|
|
// do something
|
|
}
|
|
}
|
|
|
|
documentNotFound
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
$test = new EventTest();
|
|
$evm = $dm->getEventManager();
|
|
$evm->addEventListener(Events::documentNotFound, $test);
|
|
|
|
Define the ``EventTest`` class with a ``documentNotFound()`` method:
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
class EventTest
|
|
{
|
|
public function documentNotFound(\Doctrine\ODM\MongoDB\Event\DocumentNotFoundEventArgs $eventArgs)
|
|
{
|
|
$proxy = $eventArgs->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
|
|
|
|
<?php
|
|
|
|
$test = new EventTest();
|
|
$evm = $dm->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
|
|
|
|
<?php
|
|
|
|
class EventTest
|
|
{
|
|
public function postUpdate(\Doctrine\ODM\MongoDB\Event\LifecycleEventArgs $eventArgs)
|
|
{
|
|
}
|
|
|
|
public function postRemove(\Doctrine\ODM\MongoDB\Event\LifecycleEventArgs $eventArgs)
|
|
{
|
|
}
|
|
|
|
public function postPersist(\Doctrine\ODM\MongoDB\Event\LifecycleEventArgs $eventArgs)
|
|
{
|
|
}
|
|
}
|
|
|
|
postCollectionLoad
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
.. note::
|
|
This event was introduced in version 1.1
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
$test = new EventTest();
|
|
$evm = $dm->getEventManager();
|
|
$evm->addEventListener(Events::postCollectionLoad, $test);
|
|
|
|
Define the ``EventTest`` class with a ``postCollectionLoad()`` method:
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
|
|
class EventTest
|
|
{
|
|
public function postCollectionLoad(\Doctrine\ODM\MongoDB\Event\PostCollectionLoadEventArgs $eventArgs)
|
|
{
|
|
$collection = $eventArgs->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
|
|
|
|
<?php
|
|
|
|
$test = new EventTest();
|
|
$metadataFactory = $dm->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);
|
|
}
|
|
}
|