1Events
2======
3
4Doctrine 2 features a lightweight event system that is part of the
5Common package. Doctrine uses it to dispatch system events, mainly
6:ref:`lifecycle events <reference-events-lifecycle-events>`.
7You can also use it for your own custom events.
8
9The Event System
10----------------
11
12The event system is controlled by the ``EventManager``. It is the
13central point of Doctrine's event listener system. Listeners are
14registered on the manager and events are dispatched through the
15manager.
16
17.. code-block:: php
18
19    <?php
20    $evm = new EventManager();
21
22Now we can add some event listeners to the ``$evm``. Let's create a
23``TestEvent`` class to play around with.
24
25.. code-block:: php
26
27    <?php
28    class TestEvent
29    {
30        const preFoo = 'preFoo';
31        const postFoo = 'postFoo';
32
33        private $_evm;
34
35        public $preFooInvoked = false;
36        public $postFooInvoked = false;
37
38        public function __construct($evm)
39        {
40            $evm->addEventListener(array(self::preFoo, self::postFoo), $this);
41        }
42
43        public function preFoo(EventArgs $e)
44        {
45            $this->preFooInvoked = true;
46        }
47
48        public function postFoo(EventArgs $e)
49        {
50            $this->postFooInvoked = true;
51        }
52    }
53
54    // Create a new instance
55    $test = new TestEvent($evm);
56
57Events can be dispatched by using the ``dispatchEvent()`` method.
58
59.. code-block:: php
60
61    <?php
62    $evm->dispatchEvent(TestEvent::preFoo);
63    $evm->dispatchEvent(TestEvent::postFoo);
64
65You can easily remove a listener with the ``removeEventListener()``
66method.
67
68.. code-block:: php
69
70    <?php
71    $evm->removeEventListener(array(self::preFoo, self::postFoo), $this);
72
73The Doctrine 2 event system also has a simple concept of event
74subscribers. We can define a simple ``TestEventSubscriber`` class
75which implements the ``\Doctrine\Common\EventSubscriber`` interface
76and implements a ``getSubscribedEvents()`` method which returns an
77array of events it should be subscribed to.
78
79.. code-block:: php
80
81    <?php
82    class TestEventSubscriber implements \Doctrine\Common\EventSubscriber
83    {
84        public $preFooInvoked = false;
85
86        public function preFoo()
87        {
88            $this->preFooInvoked = true;
89        }
90
91        public function getSubscribedEvents()
92        {
93            return array(TestEvent::preFoo);
94        }
95    }
96
97    $eventSubscriber = new TestEventSubscriber();
98    $evm->addEventSubscriber($eventSubscriber);
99
100.. note::
101
102    The array to return in the ``getSubscribedEvents`` method is a simple array
103    with the values being the event names. The subscriber must have a method
104    that is named exactly like the event.
105
106Now when you dispatch an event, any event subscribers will be
107notified for that event.
108
109.. code-block:: php
110
111    <?php
112    $evm->dispatchEvent(TestEvent::preFoo);
113
114Now you can test the ``$eventSubscriber`` instance to see if the
115``preFoo()`` method was invoked.
116
117.. code-block:: php
118
119    <?php
120    if ($eventSubscriber->preFooInvoked) {
121        echo 'pre foo invoked!';
122    }
123
124Naming convention
125~~~~~~~~~~~~~~~~~
126
127Events being used with the Doctrine 2 EventManager are best named
128with camelcase and the value of the corresponding constant should
129be the name of the constant itself, even with spelling. This has
130several reasons:
131
132
133-  It is easy to read.
134-  Simplicity.
135-  Each method within an EventSubscriber is named after the
136   corresponding constant's value. If the constant's name and value differ
137   it contradicts the intention of using the constant and makes your code
138   harder to maintain.
139
140An example for a correct notation can be found in the example
141``TestEvent`` above.
142
143.. _reference-events-lifecycle-events:
144
145Lifecycle Events
146----------------
147
148The EntityManager and UnitOfWork trigger a bunch of events during
149the life-time of their registered entities.
150
151
152-  preRemove - The preRemove event occurs for a given entity before
153   the respective EntityManager remove operation for that entity is
154   executed.  It is not called for a DQL DELETE statement.
155-  postRemove - The postRemove event occurs for an entity after the
156   entity has been deleted. It will be invoked after the database
157   delete operations. It is not called for a DQL DELETE statement.
158-  prePersist - The prePersist event occurs for a given entity
159   before the respective EntityManager persist operation for that
160   entity is executed. It should be noted that this event is only triggered on
161   *initial* persist of an entity (i.e. it does not trigger on future updates).
162-  postPersist - The postPersist event occurs for an entity after
163   the entity has been made persistent. It will be invoked after the
164   database insert operations. Generated primary key values are
165   available in the postPersist event.
166-  preUpdate - The preUpdate event occurs before the database
167   update operations to entity data. It is not called for a DQL UPDATE statement.
168-  postUpdate - The postUpdate event occurs after the database
169   update operations to entity data. It is not called for a DQL UPDATE statement.
170-  postLoad - The postLoad event occurs for an entity after the
171   entity has been loaded into the current EntityManager from the
172   database or after the refresh operation has been applied to it.
173-  loadClassMetadata - The loadClassMetadata event occurs after the
174   mapping metadata for a class has been loaded from a mapping source
175   (annotations/xml/yaml). This event is not a lifecycle callback.
176-  onClassMetadataNotFound - Loading class metadata for a particular
177   requested class name failed. Manipulating the given event args instance
178   allows providing fallback metadata even when no actual metadata exists
179   or could be found. This event is not a lifecycle callback.
180-  preFlush - The preFlush event occurs at the very beginning of a flush
181   operation. This event is not a lifecycle callback.
182-  onFlush - The onFlush event occurs after the change-sets of all
183   managed entities are computed. This event is not a lifecycle
184   callback.
185-  postFlush - The postFlush event occurs at the end of a flush operation. This
186   event is not a lifecycle callback.
187-  onClear - The onClear event occurs when the EntityManager#clear() operation is
188   invoked, after all references to entities have been removed from the unit of
189   work. This event is not a lifecycle callback.
190
191.. warning::
192
193    Note that, when using ``Doctrine\ORM\AbstractQuery#iterate()``, ``postLoad``
194    events will be executed immediately after objects are being hydrated, and therefore
195    associations are not guaranteed to be initialized. It is not safe to combine
196    usage of ``Doctrine\ORM\AbstractQuery#iterate()`` and ``postLoad`` event
197    handlers.
198
199.. warning::
200
201    Note that the postRemove event or any events triggered after an entity removal
202    can receive an uninitializable proxy in case you have configured an entity to
203    cascade remove relations. In this case, you should load yourself the proxy in
204    the associated pre event.
205
206You can access the Event constants from the ``Events`` class in the
207ORM package.
208
209.. code-block:: php
210
211    <?php
212    use Doctrine\ORM\Events;
213    echo Events::preUpdate;
214
215These can be hooked into by two different types of event
216listeners:
217
218-  Lifecycle Callbacks are methods on the entity classes that are
219   called when the event is triggered. As of v2.4 they receive some kind
220   of ``EventArgs`` instance.
221-  Lifecycle Event Listeners and Subscribers are classes with specific callback
222   methods that receives some kind of ``EventArgs`` instance.
223
224The EventArgs instance received by the listener gives access to the entity,
225EntityManager and other relevant data.
226
227.. note::
228
229    All Lifecycle events that happen during the ``flush()`` of
230    an EntityManager have very specific constraints on the allowed
231    operations that can be executed. Please read the
232    :ref:`reference-events-implementing-listeners` section very carefully
233    to understand which operations are allowed in which lifecycle event.
234
235
236Lifecycle Callbacks
237-------------------
238
239Lifecycle Callbacks are defined on an entity class. They allow you to
240trigger callbacks whenever an instance of that entity class experiences
241a relevant lifecycle event. More than one callback can be defined for each
242lifecycle event. Lifecycle Callbacks are best used for simple operations
243specific to a particular entity class's lifecycle.
244
245.. code-block:: php
246
247    <?php
248
249    /** @Entity @HasLifecycleCallbacks */
250    class User
251    {
252        // ...
253
254        /**
255         * @Column(type="string", length=255)
256         */
257        public $value;
258
259        /** @Column(name="created_at", type="string", length=255) */
260        private $createdAt;
261
262        /** @PrePersist */
263        public function doStuffOnPrePersist()
264        {
265            $this->createdAt = date('Y-m-d H:i:s');
266        }
267
268        /** @PrePersist */
269        public function doOtherStuffOnPrePersist()
270        {
271            $this->value = 'changed from prePersist callback!';
272        }
273
274        /** @PostPersist */
275        public function doStuffOnPostPersist()
276        {
277            $this->value = 'changed from postPersist callback!';
278        }
279
280        /** @PostLoad */
281        public function doStuffOnPostLoad()
282        {
283            $this->value = 'changed from postLoad callback!';
284        }
285
286        /** @PreUpdate */
287        public function doStuffOnPreUpdate()
288        {
289            $this->value = 'changed from preUpdate callback!';
290        }
291    }
292
293Note that the methods set as lifecycle callbacks need to be public and,
294when using these annotations, you have to apply the
295``@HasLifecycleCallbacks`` marker annotation on the entity class.
296
297If you want to register lifecycle callbacks from YAML or XML you
298can do it with the following.
299
300.. code-block:: yaml
301
302    User:
303      type: entity
304      fields:
305    # ...
306        name:
307          type: string(50)
308      lifecycleCallbacks:
309        prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ]
310        postPersist: [ doStuffOnPostPersist ]
311
312In YAML the ``key`` of the lifecycleCallbacks entry is the event that you
313are triggering on and the value is the method (or methods) to call. The allowed
314event types are the ones listed in the previous Lifecycle Events section.
315
316XML would look something like this:
317
318.. code-block:: xml
319
320    <?xml version="1.0" encoding="UTF-8"?>
321
322    <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
323          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
324          xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
325                              /Users/robo/dev/php/Doctrine/doctrine-mapping.xsd">
326
327        <entity name="User">
328
329            <lifecycle-callbacks>
330                <lifecycle-callback type="prePersist" method="doStuffOnPrePersist"/>
331                <lifecycle-callback type="postPersist" method="doStuffOnPostPersist"/>
332            </lifecycle-callbacks>
333
334        </entity>
335
336    </doctrine-mapping>
337
338In XML the ``type`` of the lifecycle-callback entry is the event that you
339are triggering on and the ``method`` is the method to call. The allowed event
340types are the ones listed in the previous Lifecycle Events section.
341
342When using YAML or XML you need to remember to create public methods to match the
343callback names you defined. E.g. in these examples ``doStuffOnPrePersist()``,
344``doOtherStuffOnPrePersist()`` and ``doStuffOnPostPersist()`` methods need to be
345defined on your ``User`` model.
346
347.. code-block:: php
348
349    <?php
350    // ...
351
352    class User
353    {
354        // ...
355
356        public function doStuffOnPrePersist()
357        {
358            // ...
359        }
360
361        public function doOtherStuffOnPrePersist()
362        {
363            // ...
364        }
365
366        public function doStuffOnPostPersist()
367        {
368            // ...
369        }
370    }
371
372
373Lifecycle Callbacks Event Argument
374-----------------------------------
375
376.. versionadded:: 2.4
377
378Since 2.4 the triggered event is given to the lifecycle-callback.
379
380With the additional argument you have access to the
381``EntityManager`` and ``UnitOfWork`` APIs inside these callback methods.
382
383.. code-block:: php
384
385    <?php
386    // ...
387
388    class User
389    {
390        public function preUpdate(PreUpdateEventArgs $event)
391        {
392            if ($event->hasChangedField('username')) {
393                // Do something when the username is changed.
394            }
395        }
396    }
397
398Listening and subscribing to Lifecycle Events
399---------------------------------------------
400
401Lifecycle event listeners are much more powerful than the simple
402lifecycle callbacks that are defined on the entity classes. They
403sit at a level above the entities and allow you to implement re-usable
404behaviors across different entity classes.
405
406Note that they require much more detailed knowledge about the inner
407workings of the EntityManager and UnitOfWork. Please read the
408*Implementing Event Listeners* section carefully if you are trying
409to write your own listener.
410
411For event subscribers, there are no surprises. They declare the
412lifecycle events in their ``getSubscribedEvents`` method and provide
413public methods that expect the relevant arguments.
414
415A lifecycle event listener looks like the following:
416
417.. code-block:: php
418
419    <?php
420    use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
421
422    class MyEventListener
423    {
424        public function preUpdate(LifecycleEventArgs $args)
425        {
426            $entity = $args->getObject();
427            $entityManager = $args->getObjectManager();
428
429            // perhaps you only want to act on some "Product" entity
430            if ($entity instanceof Product) {
431                // do something with the Product
432            }
433        }
434    }
435
436A lifecycle event subscriber may looks like this:
437
438.. code-block:: php
439
440    <?php
441    use Doctrine\ORM\Events;
442    use Doctrine\Common\EventSubscriber;
443    use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
444
445    class MyEventSubscriber implements EventSubscriber
446    {
447        public function getSubscribedEvents()
448        {
449            return array(
450                Events::postUpdate,
451            );
452        }
453
454        public function postUpdate(LifecycleEventArgs $args)
455        {
456            $entity = $args->getObject();
457            $entityManager = $args->getObjectManager();
458
459            // perhaps you only want to act on some "Product" entity
460            if ($entity instanceof Product) {
461                // do something with the Product
462            }
463        }
464
465.. note::
466
467    Lifecycle events are triggered for all entities. It is the responsibility
468    of the listeners and subscribers to check if the entity is of a type
469    it wants to handle.
470
471To register an event listener or subscriber, you have to hook it into the
472EventManager that is passed to the EntityManager factory:
473
474.. code-block:: php
475
476    <?php
477    $eventManager = new EventManager();
478    $eventManager->addEventListener(array(Events::preUpdate), new MyEventListener());
479    $eventManager->addEventSubscriber(new MyEventSubscriber());
480
481    $entityManager = EntityManager::create($dbOpts, $config, $eventManager);
482
483You can also retrieve the event manager instance after the
484EntityManager was created:
485
486.. code-block:: php
487
488    <?php
489    $entityManager->getEventManager()->addEventListener(array(Events::preUpdate), new MyEventListener());
490    $entityManager->getEventManager()->addEventSubscriber(new MyEventSubscriber());
491
492.. _reference-events-implementing-listeners:
493
494Implementing Event Listeners
495----------------------------
496
497This section explains what is and what is not allowed during
498specific lifecycle events of the UnitOfWork. Although you get
499passed the EntityManager in all of these events, you have to follow
500these restrictions very carefully since operations in the wrong
501event may produce lots of different errors, such as inconsistent
502data and lost updates/persists/removes.
503
504For the described events that are also lifecycle callback events
505the restrictions apply as well, with the additional restriction
506that (prior to version 2.4) you do not have access to the
507EntityManager or UnitOfWork APIs inside these events.
508
509prePersist
510~~~~~~~~~~
511
512There are two ways for the ``prePersist`` event to be triggered.
513One is obviously when you call ``EntityManager#persist()``. The
514event is also called for all cascaded associations.
515
516There is another way for ``prePersist`` to be called, inside the
517``flush()`` method when changes to associations are computed and
518this association is marked as cascade persist. Any new entity found
519during this operation is also persisted and ``prePersist`` called
520on it. This is called "persistence by reachability".
521
522In both cases you get passed a ``LifecycleEventArgs`` instance
523which has access to the entity and the entity manager.
524
525The following restrictions apply to ``prePersist``:
526
527
528-  If you are using a PrePersist Identity Generator such as
529   sequences the ID value will *NOT* be available within any
530   PrePersist events.
531-  Doctrine will not recognize changes made to relations in a prePersist
532   event. This includes modifications to
533   collections such as additions, removals or replacement.
534
535preRemove
536~~~~~~~~~
537
538The ``preRemove`` event is called on every entity when its passed
539to the ``EntityManager#remove()`` method. It is cascaded for all
540associations that are marked as cascade delete.
541
542There are no restrictions to what methods can be called inside the
543``preRemove`` event, except when the remove method itself was
544called during a flush operation.
545
546preFlush
547~~~~~~~~
548
549``preFlush`` is called at ``EntityManager#flush()`` before
550anything else. ``EntityManager#flush()`` can be called safely
551inside its listeners.
552
553.. code-block:: php
554
555    <?php
556
557    use Doctrine\ORM\Event\PreFlushEventArgs;
558
559    class PreFlushExampleListener
560    {
561        public function preFlush(PreFlushEventArgs $args)
562        {
563            // ...
564        }
565    }
566
567onFlush
568~~~~~~~
569
570OnFlush is a very powerful event. It is called inside
571``EntityManager#flush()`` after the changes to all the managed
572entities and their associations have been computed. This means, the
573``onFlush`` event has access to the sets of:
574
575
576-  Entities scheduled for insert
577-  Entities scheduled for update
578-  Entities scheduled for removal
579-  Collections scheduled for update
580-  Collections scheduled for removal
581
582To make use of the onFlush event you have to be familiar with the
583internal UnitOfWork API, which grants you access to the previously
584mentioned sets. See this example:
585
586.. code-block:: php
587
588    <?php
589    class FlushExampleListener
590    {
591        public function onFlush(OnFlushEventArgs $eventArgs)
592        {
593            $em = $eventArgs->getEntityManager();
594            $uow = $em->getUnitOfWork();
595
596            foreach ($uow->getScheduledEntityInsertions() as $entity) {
597
598            }
599
600            foreach ($uow->getScheduledEntityUpdates() as $entity) {
601
602            }
603
604            foreach ($uow->getScheduledEntityDeletions() as $entity) {
605
606            }
607
608            foreach ($uow->getScheduledCollectionDeletions() as $col) {
609
610            }
611
612            foreach ($uow->getScheduledCollectionUpdates() as $col) {
613
614            }
615        }
616    }
617
618The following restrictions apply to the onFlush event:
619
620
621-  If you create and persist a new entity in ``onFlush``, then
622   calling ``EntityManager#persist()`` is not enough.
623   You have to execute an additional call to
624   ``$unitOfWork->computeChangeSet($classMetadata, $entity)``.
625-  Changing primitive fields or associations requires you to
626   explicitly trigger a re-computation of the changeset of the
627   affected entity. This can be done by calling
628   ``$unitOfWork->recomputeSingleEntityChangeSet($classMetadata, $entity)``.
629
630postFlush
631~~~~~~~~~
632
633``postFlush`` is called at the end of ``EntityManager#flush()``.
634``EntityManager#flush()`` can **NOT** be called safely inside its listeners.
635
636.. code-block:: php
637
638    <?php
639
640    use Doctrine\ORM\Event\PostFlushEventArgs;
641
642    class PostFlushExampleListener
643    {
644        public function postFlush(PostFlushEventArgs $args)
645        {
646            // ...
647        }
648    }
649
650preUpdate
651~~~~~~~~~
652
653PreUpdate is the most restrictive to use event, since it is called
654right before an update statement is called for an entity inside the
655``EntityManager#flush()`` method.
656
657Changes to associations of the updated entity are never allowed in
658this event, since Doctrine cannot guarantee to correctly handle
659referential integrity at this point of the flush operation. This
660event has a powerful feature however, it is executed with a
661``PreUpdateEventArgs`` instance, which contains a reference to the
662computed change-set of this entity.
663
664This means you have access to all the fields that have changed for
665this entity with their old and new value. The following methods are
666available on the ``PreUpdateEventArgs``:
667
668
669-  ``getEntity()`` to get access to the actual entity.
670-  ``getEntityChangeSet()`` to get a copy of the changeset array.
671   Changes to this returned array do not affect updating.
672-  ``hasChangedField($fieldName)`` to check if the given field name
673   of the current entity changed.
674-  ``getOldValue($fieldName)`` and ``getNewValue($fieldName)`` to
675   access the values of a field.
676-  ``setNewValue($fieldName, $value)`` to change the value of a
677   field to be updated.
678
679A simple example for this event looks like:
680
681.. code-block:: php
682
683    <?php
684    class NeverAliceOnlyBobListener
685    {
686        public function preUpdate(PreUpdateEventArgs $eventArgs)
687        {
688            if ($eventArgs->getEntity() instanceof User) {
689                if ($eventArgs->hasChangedField('name') && $eventArgs->getNewValue('name') == 'Alice') {
690                    $eventArgs->setNewValue('name', 'Bob');
691                }
692            }
693        }
694    }
695
696You could also use this listener to implement validation of all the
697fields that have changed. This is more efficient than using a
698lifecycle callback when there are expensive validations to call:
699
700.. code-block:: php
701
702    <?php
703    class ValidCreditCardListener
704    {
705        public function preUpdate(PreUpdateEventArgs $eventArgs)
706        {
707            if ($eventArgs->getEntity() instanceof Account) {
708                if ($eventArgs->hasChangedField('creditCard')) {
709                    $this->validateCreditCard($eventArgs->getNewValue('creditCard'));
710                }
711            }
712        }
713
714        private function validateCreditCard($no)
715        {
716            // throw an exception to interrupt flush event. Transaction will be rolled back.
717        }
718    }
719
720Restrictions for this event:
721
722
723-  Changes to associations of the passed entities are not
724   recognized by the flush operation anymore.
725-  Changes to fields of the passed entities are not recognized by
726   the flush operation anymore, use the computed change-set passed to
727   the event to modify primitive field values, e.g. use
728   ``$eventArgs->setNewValue($field, $value);`` as in the Alice to Bob example above.
729-  Any calls to ``EntityManager#persist()`` or
730   ``EntityManager#remove()``, even in combination with the UnitOfWork
731   API are strongly discouraged and don't work as expected outside the
732   flush operation.
733
734postUpdate, postRemove, postPersist
735~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
736
737The three post events are called inside ``EntityManager#flush()``.
738Changes in here are not relevant to the persistence in the
739database, but you can use these events to alter non-persistable items,
740like non-mapped fields, logging or even associated classes that are
741directly mapped by Doctrine.
742
743postLoad
744~~~~~~~~
745
746This event is called after an entity is constructed by the
747EntityManager.
748
749Entity listeners
750----------------
751
752.. versionadded:: 2.4
753
754An entity listener is a lifecycle listener class used for an entity.
755
756- The entity listener's mapping may be applied to an entity class or mapped superclass.
757- An entity listener is defined by mapping the entity class with the corresponding mapping.
758
759.. configuration-block::
760
761    .. code-block:: php
762
763        <?php
764        namespace MyProject\Entity;
765
766        /** @Entity @EntityListeners({"UserListener"}) */
767        class User
768        {
769            // ....
770        }
771    .. code-block:: xml
772
773        <doctrine-mapping>
774            <entity name="MyProject\Entity\User">
775                <entity-listeners>
776                    <entity-listener class="UserListener"/>
777                </entity-listeners>
778                <!-- .... -->
779            </entity>
780        </doctrine-mapping>
781    .. code-block:: yaml
782
783        MyProject\Entity\User:
784          type: entity
785          entityListeners:
786            UserListener:
787          # ....
788
789.. _reference-entity-listeners:
790
791Entity listeners class
792~~~~~~~~~~~~~~~~~~~~~~
793
794An ``Entity Listener`` could be any class, by default it should be a class with a no-arg constructor.
795
796- Different from :ref:`reference-events-implementing-listeners` an ``Entity Listener`` is invoked just to the specified entity
797- An entity listener method receives two arguments, the entity instance and the lifecycle event.
798- The callback method can be defined by naming convention or specifying a method mapping.
799- When a listener mapping is not given the parser will use the naming convention to look for a matching method,
800  e.g. it will look for a public ``preUpdate()`` method if you are listening to the ``preUpdate`` event.
801- When a listener mapping is given the parser will not look for any methods using the naming convention.
802
803.. code-block:: php
804
805    <?php
806    class UserListener
807    {
808        public function preUpdate(User $user, PreUpdateEventArgs $event)
809        {
810            // Do something on pre update.
811        }
812    }
813
814To define a specific event listener method (one that does not follow the naming convention)
815you need to map the listener method using the event type mapping:
816
817.. configuration-block::
818
819    .. code-block:: php
820
821        <?php
822        class UserListener
823        {
824            /** @PrePersist */
825            public function prePersistHandler(User $user, LifecycleEventArgs $event) { // ... }
826
827            /** @PostPersist */
828            public function postPersistHandler(User $user, LifecycleEventArgs $event) { // ... }
829
830            /** @PreUpdate */
831            public function preUpdateHandler(User $user, PreUpdateEventArgs $event) { // ... }
832
833            /** @PostUpdate */
834            public function postUpdateHandler(User $user, LifecycleEventArgs $event) { // ... }
835
836            /** @PostRemove */
837            public function postRemoveHandler(User $user, LifecycleEventArgs $event) { // ... }
838
839            /** @PreRemove */
840            public function preRemoveHandler(User $user, LifecycleEventArgs $event) { // ... }
841
842            /** @PreFlush */
843            public function preFlushHandler(User $user, PreFlushEventArgs $event) { // ... }
844
845            /** @PostLoad */
846            public function postLoadHandler(User $user, LifecycleEventArgs $event) { // ... }
847        }
848    .. code-block:: xml
849
850        <doctrine-mapping>
851            <entity name="MyProject\Entity\User">
852                 <entity-listeners>
853                    <entity-listener class="UserListener">
854                        <lifecycle-callback type="preFlush"      method="preFlushHandler"/>
855                        <lifecycle-callback type="postLoad"      method="postLoadHandler"/>
856
857                        <lifecycle-callback type="postPersist"   method="postPersistHandler"/>
858                        <lifecycle-callback type="prePersist"    method="prePersistHandler"/>
859
860                        <lifecycle-callback type="postUpdate"    method="postUpdateHandler"/>
861                        <lifecycle-callback type="preUpdate"     method="preUpdateHandler"/>
862
863                        <lifecycle-callback type="postRemove"    method="postRemoveHandler"/>
864                        <lifecycle-callback type="preRemove"     method="preRemoveHandler"/>
865                    </entity-listener>
866                </entity-listeners>
867                <!-- .... -->
868            </entity>
869        </doctrine-mapping>
870    .. code-block:: yaml
871
872        MyProject\Entity\User:
873          type: entity
874          entityListeners:
875            UserListener:
876              preFlush: [preFlushHandler]
877              postLoad: [postLoadHandler]
878
879              postPersist: [postPersistHandler]
880              prePersist: [prePersistHandler]
881
882              postUpdate: [postUpdateHandler]
883              preUpdate: [preUpdateHandler]
884
885              postRemove: [postRemoveHandler]
886              preRemove: [preRemoveHandler]
887          # ....
888
889
890
891Entity listeners resolver
892~~~~~~~~~~~~~~~~~~~~~~~~~~
893Doctrine invokes the listener resolver to get the listener instance.
894
895- A resolver allows you register a specific entity listener instance.
896- You can also implement your own resolver by extending ``Doctrine\ORM\Mapping\DefaultEntityListenerResolver`` or implementing ``Doctrine\ORM\Mapping\EntityListenerResolver``
897
898Specifying an entity listener instance :
899
900.. code-block:: php
901
902    <?php
903    // User.php
904
905    /** @Entity @EntityListeners({"UserListener"}) */
906    class User
907    {
908        // ....
909    }
910
911    // UserListener.php
912    class UserListener
913    {
914        public function __construct(MyService $service)
915        {
916            $this->service = $service;
917        }
918
919        public function preUpdate(User $user, PreUpdateEventArgs $event)
920        {
921            $this->service->doSomething($user);
922        }
923    }
924
925    // register a entity listener.
926    $listener = $container->get('user_listener');
927    $em->getConfiguration()->getEntityListenerResolver()->register($listener);
928
929Implementing your own resolver :
930
931.. code-block:: php
932
933    <?php
934    class MyEntityListenerResolver extends \Doctrine\ORM\Mapping\DefaultEntityListenerResolver
935    {
936        public function __construct($container)
937        {
938            $this->container = $container;
939        }
940
941        public function resolve($className)
942        {
943            // resolve the service id by the given class name;
944            $id = 'user_listener';
945
946            return $this->container->get($id);
947        }
948    }
949
950    // Configure the listener resolver only before instantiating the EntityManager
951    $configurations->setEntityListenerResolver(new MyEntityListenerResolver);
952    EntityManager::create(.., $configurations, ..);
953
954Load ClassMetadata Event
955------------------------
956
957When the mapping information for an entity is read, it is populated
958in to a ``ClassMetadataInfo`` instance. You can hook in to this
959process and manipulate the instance.
960
961.. code-block:: php
962
963    <?php
964    $test = new TestEvent();
965    $metadataFactory = $em->getMetadataFactory();
966    $evm = $em->getEventManager();
967    $evm->addEventListener(Events::loadClassMetadata, $test);
968
969    class TestEvent
970    {
971        public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs)
972        {
973            $classMetadata = $eventArgs->getClassMetadata();
974            $fieldMapping = array(
975                'fieldName' => 'about',
976                'type' => 'string',
977                'length' => 255
978            );
979            $classMetadata->mapField($fieldMapping);
980        }
981    }
982
983
984