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