Merge pull request #621 from dbu/event-subscribers
[doc] adding some more doc and examples for lifecycle event listeners and subscribers
This commit is contained in:
commit
3fef8d7285
@ -2,7 +2,9 @@ Events
|
|||||||
======
|
======
|
||||||
|
|
||||||
Doctrine 2 features a lightweight event system that is part of the
|
Doctrine 2 features a lightweight event system that is part of the
|
||||||
Common package.
|
Common package. Doctrine uses it to dispatch system events, mainly
|
||||||
|
:ref:`lifecycle events <reference-events-lifecycle-events>`.
|
||||||
|
You can also use it for your own custom events.
|
||||||
|
|
||||||
The Event System
|
The Event System
|
||||||
----------------
|
----------------
|
||||||
@ -27,28 +29,28 @@ Now we can add some event listeners to the ``$evm``. Let's create a
|
|||||||
{
|
{
|
||||||
const preFoo = 'preFoo';
|
const preFoo = 'preFoo';
|
||||||
const postFoo = 'postFoo';
|
const postFoo = 'postFoo';
|
||||||
|
|
||||||
private $_evm;
|
private $_evm;
|
||||||
|
|
||||||
public $preFooInvoked = false;
|
public $preFooInvoked = false;
|
||||||
public $postFooInvoked = false;
|
public $postFooInvoked = false;
|
||||||
|
|
||||||
public function __construct($evm)
|
public function __construct($evm)
|
||||||
{
|
{
|
||||||
$evm->addEventListener(array(self::preFoo, self::postFoo), $this);
|
$evm->addEventListener(array(self::preFoo, self::postFoo), $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function preFoo(EventArgs $e)
|
public function preFoo(EventArgs $e)
|
||||||
{
|
{
|
||||||
$this->preFooInvoked = true;
|
$this->preFooInvoked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function postFoo(EventArgs $e)
|
public function postFoo(EventArgs $e)
|
||||||
{
|
{
|
||||||
$this->postFooInvoked = true;
|
$this->postFooInvoked = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new instance
|
// Create a new instance
|
||||||
$test = new EventTest($evm);
|
$test = new EventTest($evm);
|
||||||
|
|
||||||
@ -80,22 +82,28 @@ array of events it should be subscribed to.
|
|||||||
class TestEventSubscriber implements \Doctrine\Common\EventSubscriber
|
class TestEventSubscriber implements \Doctrine\Common\EventSubscriber
|
||||||
{
|
{
|
||||||
public $preFooInvoked = false;
|
public $preFooInvoked = false;
|
||||||
|
|
||||||
public function preFoo()
|
public function preFoo()
|
||||||
{
|
{
|
||||||
$this->preFooInvoked = true;
|
$this->preFooInvoked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSubscribedEvents()
|
public function getSubscribedEvents()
|
||||||
{
|
{
|
||||||
return array(TestEvent::preFoo);
|
return array(TestEvent::preFoo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$eventSubscriber = new TestEventSubscriber();
|
$eventSubscriber = new TestEventSubscriber();
|
||||||
$evm->addEventSubscriber($eventSubscriber);
|
$evm->addEventSubscriber($eventSubscriber);
|
||||||
|
|
||||||
Now when you dispatch an event any event subscribers will be
|
.. note::
|
||||||
|
|
||||||
|
The array to return in the ``getSubscribedEvents`` method is a simple array
|
||||||
|
with the values being the event names. The subscriber must have a method
|
||||||
|
that is named exactly like the event.
|
||||||
|
|
||||||
|
Now when you dispatch an event, any event subscribers will be
|
||||||
notified for that event.
|
notified for that event.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
@ -133,6 +141,8 @@ several reasons:
|
|||||||
An example for a correct notation can be found in the example
|
An example for a correct notation can be found in the example
|
||||||
``EventTest`` above.
|
``EventTest`` above.
|
||||||
|
|
||||||
|
.. _reference-events-lifecycle-events:
|
||||||
|
|
||||||
Lifecycle Events
|
Lifecycle Events
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
@ -164,7 +174,7 @@ the life-time of their registered entities.
|
|||||||
- loadClassMetadata - The loadClassMetadata event occurs after the
|
- loadClassMetadata - The loadClassMetadata event occurs after the
|
||||||
mapping metadata for a class has been loaded from a mapping source
|
mapping metadata for a class has been loaded from a mapping source
|
||||||
(annotations/xml/yaml).
|
(annotations/xml/yaml).
|
||||||
- preFlush - The preFlush event occurs at the very beginning of a flush
|
- preFlush - The preFlush event occurs at the very beginning of a flush
|
||||||
operation. This event is not a lifecycle callback.
|
operation. This event is not a lifecycle callback.
|
||||||
- onFlush - The onFlush event occurs after the change-sets of all
|
- onFlush - The onFlush event occurs after the change-sets of all
|
||||||
managed entities are computed. This event is not a lifecycle
|
managed entities are computed. This event is not a lifecycle
|
||||||
@ -198,7 +208,7 @@ listeners:
|
|||||||
|
|
||||||
- Lifecycle Callbacks are methods on the entity classes that are
|
- Lifecycle Callbacks are methods on the entity classes that are
|
||||||
called when the event is triggered. They receives some kind of ``EventArgs``.
|
called when the event is triggered. They receives some kind of ``EventArgs``.
|
||||||
- Lifecycle Event Listeners are classes with specific callback
|
- Lifecycle Event Listeners and Subscribers are classes with specific callback
|
||||||
methods that receives some kind of ``EventArgs`` instance which
|
methods that receives some kind of ``EventArgs`` instance which
|
||||||
give access to the entity, EntityManager or other relevant data.
|
give access to the entity, EntityManager or other relevant data.
|
||||||
|
|
||||||
@ -222,44 +232,44 @@ event occurs.
|
|||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/** @Entity @HasLifecycleCallbacks */
|
/** @Entity @HasLifecycleCallbacks */
|
||||||
class User
|
class User
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Column(type="string", length=255)
|
* @Column(type="string", length=255)
|
||||||
*/
|
*/
|
||||||
public $value;
|
public $value;
|
||||||
|
|
||||||
/** @Column(name="created_at", type="string", length=255) */
|
/** @Column(name="created_at", type="string", length=255) */
|
||||||
private $createdAt;
|
private $createdAt;
|
||||||
|
|
||||||
/** @PrePersist */
|
/** @PrePersist */
|
||||||
public function doStuffOnPrePersist()
|
public function doStuffOnPrePersist()
|
||||||
{
|
{
|
||||||
$this->createdAt = date('Y-m-d H:i:s');
|
$this->createdAt = date('Y-m-d H:i:s');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @PrePersist */
|
/** @PrePersist */
|
||||||
public function doOtherStuffOnPrePersist()
|
public function doOtherStuffOnPrePersist()
|
||||||
{
|
{
|
||||||
$this->value = 'changed from prePersist callback!';
|
$this->value = 'changed from prePersist callback!';
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @PostPersist */
|
/** @PostPersist */
|
||||||
public function doStuffOnPostPersist()
|
public function doStuffOnPostPersist()
|
||||||
{
|
{
|
||||||
$this->value = 'changed from postPersist callback!';
|
$this->value = 'changed from postPersist callback!';
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @PostLoad */
|
/** @PostLoad */
|
||||||
public function doStuffOnPostLoad()
|
public function doStuffOnPostLoad()
|
||||||
{
|
{
|
||||||
$this->value = 'changed from postLoad callback!';
|
$this->value = 'changed from postLoad callback!';
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @PreUpdate */
|
/** @PreUpdate */
|
||||||
public function doStuffOnPreUpdate()
|
public function doStuffOnPreUpdate()
|
||||||
{
|
{
|
||||||
@ -283,28 +293,28 @@ can do it with the following.
|
|||||||
type: string(50)
|
type: string(50)
|
||||||
lifecycleCallbacks:
|
lifecycleCallbacks:
|
||||||
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ]
|
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ]
|
||||||
postPersist: [ doStuffOnPostPersist ]
|
postPersist: [ doStuffOnPostPersist ]
|
||||||
|
|
||||||
XML would look something like this:
|
XML would look something like this:
|
||||||
|
|
||||||
.. code-block:: xml
|
.. code-block:: xml
|
||||||
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||||
/Users/robo/dev/php/Doctrine/doctrine-mapping.xsd">
|
/Users/robo/dev/php/Doctrine/doctrine-mapping.xsd">
|
||||||
|
|
||||||
<entity name="User">
|
<entity name="User">
|
||||||
|
|
||||||
<lifecycle-callbacks>
|
<lifecycle-callbacks>
|
||||||
<lifecycle-callback type="prePersist" method="doStuffOnPrePersist"/>
|
<lifecycle-callback type="prePersist" method="doStuffOnPrePersist"/>
|
||||||
<lifecycle-callback type="postPersist" method="doStuffOnPostPersist"/>
|
<lifecycle-callback type="postPersist" method="doStuffOnPostPersist"/>
|
||||||
</lifecycle-callbacks>
|
</lifecycle-callbacks>
|
||||||
|
|
||||||
</entity>
|
</entity>
|
||||||
|
|
||||||
</doctrine-mapping>
|
</doctrine-mapping>
|
||||||
|
|
||||||
You just need to make sure a public ``doStuffOnPrePersist()`` and
|
You just need to make sure a public ``doStuffOnPrePersist()`` and
|
||||||
@ -315,16 +325,16 @@ model.
|
|||||||
|
|
||||||
<?php
|
<?php
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
class User
|
class User
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
public function doStuffOnPrePersist()
|
public function doStuffOnPrePersist()
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
public function doStuffOnPostPersist()
|
public function doStuffOnPostPersist()
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
@ -360,8 +370,8 @@ With the additional argument you have access to the
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Listening to Lifecycle Events
|
Listening and subscribing to Lifecycle Events
|
||||||
-----------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
Lifecycle event listeners are much more powerful than the simple
|
Lifecycle event listeners are much more powerful than the simple
|
||||||
lifecycle callbacks that are defined on the entity classes. They
|
lifecycle callbacks that are defined on the entity classes. They
|
||||||
@ -371,7 +381,67 @@ workings of the EntityManager and UnitOfWork. Please read the
|
|||||||
*Implementing Event Listeners* section carefully if you are trying
|
*Implementing Event Listeners* section carefully if you are trying
|
||||||
to write your own listener.
|
to write your own listener.
|
||||||
|
|
||||||
To register an event listener you have to hook it into the
|
For event subscribers, there are no surprises. They declare the
|
||||||
|
lifecycle events in their ``getSubscribedEvents`` method and provide
|
||||||
|
public methods that expect the relevant arguments.
|
||||||
|
|
||||||
|
A lifecycle event listener looks like the following:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
|
||||||
|
|
||||||
|
class MyEventListener
|
||||||
|
{
|
||||||
|
public function preUpdate(LifecycleEventArgs $args)
|
||||||
|
{
|
||||||
|
$entity = $args->getObject();
|
||||||
|
$entityManager = $args->getObjectManager();
|
||||||
|
|
||||||
|
// perhaps you only want to act on some "Product" entity
|
||||||
|
if ($entity instanceof Product) {
|
||||||
|
// do something with the Product
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
A lifecycle event subscriber may looks like this:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
<?php
|
||||||
|
use Doctrine\ORM\Events;
|
||||||
|
use Doctrine\Common\EventSubscriber;
|
||||||
|
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
|
||||||
|
|
||||||
|
class MyEventSubscriber implements EventSubscriber
|
||||||
|
{
|
||||||
|
public function getSubscribedEvents()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
Events::postUpdate,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function postUpdate(LifecycleEventArgs $args)
|
||||||
|
{
|
||||||
|
$entity = $args->getObject();
|
||||||
|
$entityManager = $args->getObjectManager();
|
||||||
|
|
||||||
|
// perhaps you only want to act on some "Product" entity
|
||||||
|
if ($entity instanceof Product) {
|
||||||
|
// do something with the Product
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Lifecycle events are triggered for all entities. It is the responsibility
|
||||||
|
of the listeners and subscribers to check if the entity is of a type
|
||||||
|
it wants to handle.
|
||||||
|
|
||||||
|
To register an event listener or subscriber, you have to hook it into the
|
||||||
EventManager that is passed to the EntityManager factory:
|
EventManager that is passed to the EntityManager factory:
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
@ -380,7 +450,7 @@ EventManager that is passed to the EntityManager factory:
|
|||||||
$eventManager = new EventManager();
|
$eventManager = new EventManager();
|
||||||
$eventManager->addEventListener(array(Events::preUpdate), new MyEventListener());
|
$eventManager->addEventListener(array(Events::preUpdate), new MyEventListener());
|
||||||
$eventManager->addEventSubscriber(new MyEventSubscriber());
|
$eventManager->addEventSubscriber(new MyEventSubscriber());
|
||||||
|
|
||||||
$entityManager = EntityManager::create($dbOpts, $config, $eventManager);
|
$entityManager = EntityManager::create($dbOpts, $config, $eventManager);
|
||||||
|
|
||||||
You can also retrieve the event manager instance after the
|
You can also retrieve the event manager instance after the
|
||||||
@ -451,8 +521,8 @@ called during a flush operation.
|
|||||||
preFlush
|
preFlush
|
||||||
~~~~~~~~
|
~~~~~~~~
|
||||||
|
|
||||||
``preFlush`` is called at ``EntityManager#flush()`` before
|
``preFlush`` is called at ``EntityManager#flush()`` before
|
||||||
anything else. ``EntityManager#flush()`` can be called safely
|
anything else. ``EntityManager#flush()`` can be called safely
|
||||||
inside its listeners.
|
inside its listeners.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
@ -497,25 +567,25 @@ mentioned sets. See this example:
|
|||||||
{
|
{
|
||||||
$em = $eventArgs->getEntityManager();
|
$em = $eventArgs->getEntityManager();
|
||||||
$uow = $em->getUnitOfWork();
|
$uow = $em->getUnitOfWork();
|
||||||
|
|
||||||
foreach ($uow->getScheduledEntityInsertions() AS $entity) {
|
foreach ($uow->getScheduledEntityInsertions() AS $entity) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($uow->getScheduledEntityUpdates() AS $entity) {
|
foreach ($uow->getScheduledEntityUpdates() AS $entity) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($uow->getScheduledEntityDeletions() AS $entity) {
|
foreach ($uow->getScheduledEntityDeletions() AS $entity) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($uow->getScheduledCollectionDeletions() AS $col) {
|
foreach ($uow->getScheduledCollectionDeletions() AS $col) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($uow->getScheduledCollectionUpdates() AS $col) {
|
foreach ($uow->getScheduledCollectionUpdates() AS $col) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -615,7 +685,7 @@ lifecycle callback when there are expensive validations to call:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function validateCreditCard($no)
|
private function validateCreditCard($no)
|
||||||
{
|
{
|
||||||
// throw an exception to interrupt flush event. Transaction will be rolled back.
|
// throw an exception to interrupt flush event. Transaction will be rolled back.
|
||||||
@ -788,7 +858,7 @@ you should map the listener method using the event type mapping.
|
|||||||
postRemove: [postRemoveHandler]
|
postRemove: [postRemoveHandler]
|
||||||
preRemove: [preRemoveHandler]
|
preRemove: [preRemoveHandler]
|
||||||
# ....
|
# ....
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Entity listeners resolver
|
Entity listeners resolver
|
||||||
@ -867,7 +937,7 @@ process and manipulate the instance.
|
|||||||
$metadataFactory = $em->getMetadataFactory();
|
$metadataFactory = $em->getMetadataFactory();
|
||||||
$evm = $em->getEventManager();
|
$evm = $em->getEventManager();
|
||||||
$evm->addEventListener(Events::loadClassMetadata, $test);
|
$evm->addEventListener(Events::loadClassMetadata, $test);
|
||||||
|
|
||||||
class EventTest
|
class EventTest
|
||||||
{
|
{
|
||||||
public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs)
|
public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user