events.rst 30 KB

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