Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

DDC-1955 - @EntityListeners #423

Merged
merged 41 commits into from Feb 2, 2013
Commits
Jump to file or symbol
Failed to load files and symbols.
+2,162 −164
Split
@@ -196,9 +196,7 @@ listeners:
- Lifecycle Callbacks are methods on the entity classes that are
- called when the event is triggered. They receive absolutely no
- arguments and are specifically designed to allow changes inside the
- entity classes state.
+ called when the event is triggered. They receives some kind of ``EventArgs``.
- Lifecycle Event Listeners are classes with specific callback
methods that receives some kind of ``EventArgs`` instance which
give access to the entity, EntityManager or other relevant data.
@@ -336,6 +334,31 @@ The ``key`` of the lifecycleCallbacks is the name of the method and
the value is the event type. The allowed event types are the ones
listed in the previous Lifecycle Events section.
+.. versionadded:: 2.4
+
+Lifecycle Callbacks Event Argument
+-----------------------------------
+
+Since 2.4 the triggered event is given to the lifecycle-callback.
+
+With the additional argument you have access to the
+``EntityManager`` and ``UnitOfWork`` APIs inside these callback methods.
+
+.. code-block:: php
+
+ <?php
+ // ...
+
+ class User
+ {
+ public function preUpdate(PreUpdateEventArgs $event)
+ {
+ if ($event->hasChangedField('username')) {
+ // Do something when the username is changed.
+ }
+ }
+ }
+
Listening to Lifecycle Events
-----------------------------
@@ -626,6 +649,207 @@ postLoad
This event is called after an entity is constructed by the
EntityManager.
+Entity listeners
+----------------
+
+An entity listeners is a lifecycle listener classes used for an entity.
+
+- The entity listeners mapping may be applied to an entity class or mapped superclass.
+- An entity listener is defined by mapping the entity class with the corresponding mapping.
+
+.. configuration-block::
+
+ .. code-block:: php
+
+ <?php
+ namespace MyProject\Entity;
+
+ /** @Entity @EntityListeners({"UserListener"}) */
+ class User
+ {
+ // ....
+ }
+ .. code-block:: xml
+
+ <doctrine-mapping>
+ <entity name="MyProject\Entity\User">
+ <entity-listeners>
+ <entity-listener class="UserListener"/>
+ </entity-listeners>
+ <!-- .... -->
+ </entity>
+ </doctrine-mapping>
+ .. code-block:: yaml
+
+ MyProject\Entity\User:
+ type: entity
+ entityListeners:
+ UserListener:
+ # ....
+
+.. _reference-entity-listeners:
+
+Entity listeners class
+~~~~~~~~~~~~~~~~~~~~~~
+
+An ``Entity Listener`` could be any class, by default it should be a class with a no-arg constructor.
+
+- Different from :ref:`reference-events-implementing-listeners` an ``Entity Listener`` is invoked just to the specified entity
+- An entity listener method receives two arguments, the entity instance and the lifecycle event.
+- A callback method could be defined by naming convention or specifying a method mapping.
+- When the listener mapping is not given the parser will lookup for methods that match with the naming convention.
+- When the listener mapping is given the parser won't lookup for any naming convention.
+
+.. code-block:: php
+
+ <?php
+ class UserListener
+ {
+ public function preUpdate(User $user, PreUpdateEventArgs $event)
+ {
+ // Do something on pre update.
+ }
+ }
+
+To define a specific event listener method
+you should map the listener method using the event type mapping.
+
+.. configuration-block::
+
+ .. code-block:: php
+
+ <?php
+ class UserListener
+ {
+ /** @PrePersist */
+ public function prePersistHandler(User $user, LifecycleEventArgs $event) { // ... }
+
+ /** @PostPersist */
+ public function postPersistHandler(User $user, LifecycleEventArgs $event) { // ... }
+
+ /** @PreUpdate */
+ public function preUpdateHandler(User $user, PreUpdateEventArgs $event) { // ... }
+
+ /** @PostUpdate */
+ public function postUpdateHandler(User $user, LifecycleEventArgs $event) { // ... }
+
+ /** @PostRemove */
+ public function postRemoveHandler(User $user, LifecycleEventArgs $event) { // ... }
+
+ /** @PreRemove */
+ public function preRemoveHandler(User $user, LifecycleEventArgs $event) { // ... }
+
+ /** @PreFlush */
+ public function preFlushHandler(User $user, PreFlushEventArgs $event) { // ... }
+
+ /** @PostLoad */
+ public function postLoadHandler(User $user, LifecycleEventArgs $event) { // ... }
+ }
+ .. code-block:: xml
+
+ <doctrine-mapping>
+ <entity name="MyProject\Entity\User">
+ <entity-listeners>
+ <entity-listener class="UserListener">
+ <lifecycle-callback type="preFlush" method="preFlushHandler"/>
+ <lifecycle-callback type="postLoad" method="postLoadHandler"/>
+
+ <lifecycle-callback type="postPersist" method="postPersistHandler"/>
+ <lifecycle-callback type="prePersist" method="prePersistHandler"/>
+
+ <lifecycle-callback type="postUpdate" method="postUpdateHandler"/>
+ <lifecycle-callback type="preUpdate" method="preUpdateHandler"/>
+
+ <lifecycle-callback type="postRemove" method="postRemoveHandler"/>
+ <lifecycle-callback type="preRemove" method="preRemoveHandler"/>
+ </entity-listener>
+ </entity-listeners>
+ <!-- .... -->
+ </entity>
+ </doctrine-mapping>
+ .. code-block:: yaml
+
+ MyProject\Entity\User:
+ type: entity
+ entityListeners:
+ UserListener:
+ preFlush: [preFlushHandler]
+ postLoad: [postLoadHandler]
+
+ postPersist: [postPersistHandler]
+ prePersist: [prePersistHandler]
+
+ postUpdate: [postUpdateHandler]
+ preUpdate: [preUpdateHandler]
+
+ postRemove: [postRemoveHandler]
+ preRemove: [preRemoveHandler]
+ # ....
+
+
+
+Entity listeners resolver
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+Doctrine invoke the listener resolver to get the listener instance.
+
+- An resolver allows you register a specific ``Entity Listener`` instance.
+- You can also implement your own resolver by extending ``Doctrine\ORM\Mapping\DefaultEntityListenerResolver`` or implementing ``Doctrine\ORM\Mapping\EntityListenerResolver``
+
+Specifying an entity listener instance :
+
+.. code-block:: php
+
+ <?php
+ // User.php
+
+ /** @Entity @EntityListeners({"UserListener"}) */
+ class User
+ {
+ // ....
+ }
+
+ // UserListener.php
+ class UserListener
+ {
+ public function __construct(MyService $service)
+ {
+ $this->service = $service;
+ }
+
+ public function preUpdate(User $user, PreUpdateEventArgs $event)
+ {
+ $this->service->doSomething($user);
+ }
+ }
+
+ // register a entity listener.
+ $listener = $container->get('user_listener');
+ $em->getConfiguration()->getEntityListenerResolver()->register($listener);
+
+Implementing your own resolver :
+
+.. code-block:: php
+
+ <?php
+ class MyEntityListenerResolver extends \Doctrine\ORM\Mapping\DefaultEntityListenerResolver
+ {
+ public function __construct($container)
+ {
+ $this->container = $container;
+ }
+
+ public function resolve($className)
+ {
+ // resolve the service id by the given class name;
+ $id = 'user_listener';
+
+ return $this->container->get($id);
+ }
+ }
+
+ // configure the listener resolver.
+ $em->getConfiguration()->setEntityListenerResolver($container->get('my_resolver'));
+
Load ClassMetadata Event
------------------------
View
@@ -51,6 +51,7 @@
<xs:enumeration value="preRemove"/>
<xs:enumeration value="postRemove"/>
<xs:enumeration value="postLoad"/>
+ <xs:enumeration value="preFlush"/>
</xs:restriction>
</xs:simpleType>
@@ -98,6 +99,20 @@
</xs:sequence>
</xs:complexType>
+ <xs:complexType name="entity-listener">
+ <xs:sequence>
+ <xs:element name="lifecycle-callback" type="orm:lifecycle-callback" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:any minOccurs="1" maxOccurs="unbounded" namespace="##other"/>
+ </xs:sequence>
+ <xs:attribute name="class" type="xs:string"/>
+ </xs:complexType>
+
+ <xs:complexType name="entity-listeners">
+ <xs:sequence>
+ <xs:element name="entity-listener" type="orm:entity-listener" minOccurs="1" maxOccurs="unbounded" />
+ </xs:sequence>
+ </xs:complexType>
+
<xs:complexType name="column-result">
<xs:attribute name="name" type="xs:string" use="required" />
</xs:complexType>
@@ -137,6 +152,7 @@
<xs:element name="discriminator-column" type="orm:discriminator-column" minOccurs="0"/>
<xs:element name="discriminator-map" type="orm:discriminator-map" minOccurs="0"/>
<xs:element name="lifecycle-callbacks" type="orm:lifecycle-callbacks" minOccurs="0" maxOccurs="1" />
+ <xs:element name="entity-listeners" type="orm:entity-listeners" minOccurs="0" maxOccurs="1" />
<xs:element name="named-queries" type="orm:named-queries" minOccurs="0" maxOccurs="1" />
<xs:element name="named-native-queries" type="orm:named-native-queries" minOccurs="0" maxOccurs="1" />
<xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="unbounded" />
@@ -29,6 +29,8 @@
use Doctrine\ORM\Mapping\DefaultQuoteStrategy;
use Doctrine\ORM\Mapping\NamingStrategy;
use Doctrine\ORM\Mapping\DefaultNamingStrategy;
+use Doctrine\ORM\Mapping\EntityListenerResolver;
+use Doctrine\ORM\Mapping\DefaultEntityListenerResolver;
use Doctrine\Common\Annotations\SimpleAnnotationReader;
use Doctrine\Common\Annotations\CachedReader;
@@ -763,4 +765,30 @@ public function getQuoteStrategy()
return $this->_attributes['quoteStrategy'];
}
+
+ /**
+ * Set the entity listener resolver.
+ *
+ * @since 2.4
+ * @param \Doctrine\ORM\Mapping\EntityListenerResolver $resolver
+ */
+ public function setEntityListenerResolver(EntityListenerResolver $resolver)
+ {
+ $this->_attributes['entityListenerResolver'] = $resolver;
+ }
+
+ /**
+ * Get the entity listener resolver.
+ *
+ * @since 2.4
+ * @return \Doctrine\ORM\Mapping\EntityListenerResolver
+ */
+ public function getEntityListenerResolver()
+ {
+ if ( ! isset($this->_attributes['entityListenerResolver'])) {
+ $this->_attributes['entityListenerResolver'] = new DefaultEntityListenerResolver();
+ }
+
+ return $this->_attributes['entityListenerResolver'];
+ }
}
Oops, something went wrong.