Merge branch 'DDC-3033' into 2.4
This commit is contained in:
commit
cbf16f1cf8
@ -207,10 +207,13 @@ listeners:
|
|||||||
|
|
||||||
|
|
||||||
- Lifecycle Callbacks are methods on the entity classes that are
|
- Lifecycle Callbacks are methods on the entity classes that are
|
||||||
called when the event is triggered. They receives some kind of ``EventArgs``.
|
called when the event is triggered. As of v2.4 they receive some kind
|
||||||
|
of ``EventArgs`` instance.
|
||||||
- Lifecycle Event Listeners and Subscribers are classes with specific callback
|
- Lifecycle Event Listeners and Subscribers are classes with specific callback
|
||||||
methods that receives some kind of ``EventArgs`` instance which
|
methods that receives some kind of ``EventArgs`` instance.
|
||||||
give access to the entity, EntityManager or other relevant data.
|
|
||||||
|
The EventArgs instance received by the listener gives access to the entity,
|
||||||
|
EntityManager and other relevant data.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
@ -224,10 +227,11 @@ listeners:
|
|||||||
Lifecycle Callbacks
|
Lifecycle Callbacks
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
A lifecycle event is a regular event with the additional feature of
|
Lifecycle Callbacks are defined on an entity class. They allow you to
|
||||||
providing a mechanism to register direct callbacks inside the
|
trigger callbacks whenever an instance of that entity class experiences
|
||||||
corresponding entity classes that are executed when the lifecycle
|
a relevant lifecycle event. More than one callback can be defined for each
|
||||||
event occurs.
|
lifecycle event. Lifecycle Callbacks are best used for simple operations
|
||||||
|
specific to a particular entity class's lifecycle.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
@ -277,8 +281,9 @@ event occurs.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Note that when using annotations you have to apply the
|
Note that the methods set as lifecycle callbacks need to be public and,
|
||||||
@HasLifecycleCallbacks marker annotation on the entity class.
|
when using these annotations, you have to apply the
|
||||||
|
``@HasLifecycleCallbacks`` marker annotation on the entity class.
|
||||||
|
|
||||||
If you want to register lifecycle callbacks from YAML or XML you
|
If you want to register lifecycle callbacks from YAML or XML you
|
||||||
can do it with the following.
|
can do it with the following.
|
||||||
@ -295,6 +300,10 @@ can do it with the following.
|
|||||||
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ]
|
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ]
|
||||||
postPersist: [ doStuffOnPostPersist ]
|
postPersist: [ doStuffOnPostPersist ]
|
||||||
|
|
||||||
|
In YAML the ``key`` of the lifecycleCallbacks entry is the event that you
|
||||||
|
are triggering on and the value is the method (or methods) to call. The allowed
|
||||||
|
event types are the ones listed in the previous Lifecycle Events section.
|
||||||
|
|
||||||
XML would look something like this:
|
XML would look something like this:
|
||||||
|
|
||||||
.. code-block:: xml
|
.. code-block:: xml
|
||||||
@ -317,9 +326,14 @@ XML would look something like this:
|
|||||||
|
|
||||||
</doctrine-mapping>
|
</doctrine-mapping>
|
||||||
|
|
||||||
You just need to make sure a public ``doStuffOnPrePersist()`` and
|
In XML the ``type`` of the lifecycle-callback entry is the event that you
|
||||||
``doStuffOnPostPersist()`` method is defined on your ``User``
|
are triggering on and the ``method`` is the method to call. The allowed event
|
||||||
model.
|
types are the ones listed in the previous Lifecycle Events section.
|
||||||
|
|
||||||
|
When using YAML or XML you need to remember to create public methods to match the
|
||||||
|
callback names you defined. E.g. in these examples ``doStuffOnPrePersist()``,
|
||||||
|
``doOtherStuffOnPrePersist()`` and ``doStuffOnPostPersist()`` methods need to be
|
||||||
|
defined on your ``User`` model.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
@ -375,8 +389,10 @@ Listening and subscribing to Lifecycle Events
|
|||||||
|
|
||||||
Lifecycle event listeners are much more powerful than the simple
|
Lifecycle event listeners are much more powerful than the simple
|
||||||
lifecycle callbacks that are defined on the entity classes. They
|
lifecycle callbacks that are defined on the entity classes. They
|
||||||
allow to implement re-usable behaviors between different entity
|
sit at a level above the entities and allow you to implement re-usable
|
||||||
classes, yet require much more detailed knowledge about the inner
|
behaviors across different entity classes.
|
||||||
|
|
||||||
|
Note that they require much more detailed knowledge about the inner
|
||||||
workings of the EntityManager and UnitOfWork. Please read the
|
workings of the EntityManager and UnitOfWork. Please read the
|
||||||
*Implementing Event Listeners* section carefully if you are trying
|
*Implementing Event Listeners* section carefully if you are trying
|
||||||
to write your own listener.
|
to write your own listener.
|
||||||
@ -476,8 +492,8 @@ data and lost updates/persists/removes.
|
|||||||
|
|
||||||
For the described events that are also lifecycle callback events
|
For the described events that are also lifecycle callback events
|
||||||
the restrictions apply as well, with the additional restriction
|
the restrictions apply as well, with the additional restriction
|
||||||
that you do not have access to the EntityManager or UnitOfWork APIs
|
that (prior to version 2.4) you do not have access to the
|
||||||
inside these events.
|
EntityManager or UnitOfWork APIs inside these events.
|
||||||
|
|
||||||
prePersist
|
prePersist
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
@ -501,11 +517,9 @@ The following restrictions apply to ``prePersist``:
|
|||||||
- If you are using a PrePersist Identity Generator such as
|
- If you are using a PrePersist Identity Generator such as
|
||||||
sequences the ID value will *NOT* be available within any
|
sequences the ID value will *NOT* be available within any
|
||||||
PrePersist events.
|
PrePersist events.
|
||||||
- Doctrine will not recognize changes made to relations in a pre
|
- Doctrine will not recognize changes made to relations in a prePersist
|
||||||
persist event called by "reachability" through a cascade persist
|
event. This includes modifications to
|
||||||
unless you use the internal ``UnitOfWork`` API. We do not recommend
|
collections such as additions, removals or replacement.
|
||||||
such operations in the persistence by reachability context, so do
|
|
||||||
this at your own risk and possibly supported by unit-tests.
|
|
||||||
|
|
||||||
preRemove
|
preRemove
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
@ -699,7 +713,8 @@ Restrictions for this event:
|
|||||||
recognized by the flush operation anymore.
|
recognized by the flush operation anymore.
|
||||||
- Changes to fields of the passed entities are not recognized by
|
- Changes to fields of the passed entities are not recognized by
|
||||||
the flush operation anymore, use the computed change-set passed to
|
the flush operation anymore, use the computed change-set passed to
|
||||||
the event to modify primitive field values.
|
the event to modify primitive field values, e.g. use
|
||||||
|
``$eventArgs->setNewValue($field, $value);`` as in the Alice to Bob example above.
|
||||||
- Any calls to ``EntityManager#persist()`` or
|
- Any calls to ``EntityManager#persist()`` or
|
||||||
``EntityManager#remove()``, even in combination with the UnitOfWork
|
``EntityManager#remove()``, even in combination with the UnitOfWork
|
||||||
API are strongly discouraged and don't work as expected outside the
|
API are strongly discouraged and don't work as expected outside the
|
||||||
@ -769,9 +784,10 @@ An ``Entity Listener`` could be any class, by default it should be a class with
|
|||||||
|
|
||||||
- Different from :ref:`reference-events-implementing-listeners` an ``Entity Listener`` is invoked just to the specified entity
|
- 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.
|
- 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.
|
- The callback method can 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 a listener mapping is not given the parser will use the naming convention to look for a matching method,
|
||||||
- When the listener mapping is given the parser won't lookup for any naming convention.
|
e.g. it will look for a public ``preUpdate()`` method if you are listening to the ``preUpdate`` event.
|
||||||
|
- When a listener mapping is given the parser will not look for any methods using the naming convention.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
|
@ -902,7 +902,9 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
$actualData = array();
|
$actualData = array();
|
||||||
|
|
||||||
foreach ($class->reflFields as $name => $refProp) {
|
foreach ($class->reflFields as $name => $refProp) {
|
||||||
if ( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity()) {
|
if (( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity())
|
||||||
|
&& ($name !== $class->versionField)
|
||||||
|
&& ! $class->isCollectionValuedAssociation($name)) {
|
||||||
$actualData[$name] = $refProp->getValue($entity);
|
$actualData[$name] = $refProp->getValue($entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -913,9 +915,7 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
foreach ($actualData as $propName => $actualValue) {
|
foreach ($actualData as $propName => $actualValue) {
|
||||||
$orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
|
$orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
|
||||||
|
|
||||||
if (is_object($orgValue) && $orgValue !== $actualValue) {
|
if ($orgValue !== $actualValue) {
|
||||||
$changeSet[$propName] = array($orgValue, $actualValue);
|
|
||||||
} else if ($orgValue != $actualValue || ($orgValue === null ^ $actualValue === null)) {
|
|
||||||
$changeSet[$propName] = array($orgValue, $actualValue);
|
$changeSet[$propName] = array($orgValue, $actualValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
139
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3033Test.php
Normal file
139
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3033Test.php
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||||
|
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Doctrine\ORM\Event\LifecycleEventArgs;
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../../../TestInit.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group DDC-3033
|
||||||
|
*/
|
||||||
|
class DDC3033Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||||
|
{
|
||||||
|
public function testIssue()
|
||||||
|
{
|
||||||
|
$this->_schemaTool->createSchema(array(
|
||||||
|
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC3033User'),
|
||||||
|
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC3033Product'),
|
||||||
|
));
|
||||||
|
|
||||||
|
$user = new DDC3033User();
|
||||||
|
$user->name = "Test User";
|
||||||
|
$this->_em->persist($user);
|
||||||
|
|
||||||
|
$user2 = new DDC3033User();
|
||||||
|
$user2->name = "Test User 2";
|
||||||
|
$this->_em->persist($user2);
|
||||||
|
|
||||||
|
$product = new DDC3033Product();
|
||||||
|
$product->title = "Test product";
|
||||||
|
$product->buyers[] = $user;
|
||||||
|
|
||||||
|
$this->_em->persist($product);
|
||||||
|
$this->_em->flush();
|
||||||
|
|
||||||
|
$product->title = "Test Change title";
|
||||||
|
$product->buyers[] = $user2;
|
||||||
|
|
||||||
|
$this->_em->persist($product);
|
||||||
|
$this->_em->flush();
|
||||||
|
|
||||||
|
$expect = array(
|
||||||
|
'title' => array(
|
||||||
|
0 => 'Test product',
|
||||||
|
1 => 'Test Change title',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals($expect, $product->changeSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Table
|
||||||
|
* @Entity @HasLifecycleCallbacks
|
||||||
|
*/
|
||||||
|
class DDC3033Product
|
||||||
|
{
|
||||||
|
public $changeSet = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var integer $id
|
||||||
|
*
|
||||||
|
* @Column(name="id", type="integer")
|
||||||
|
* @Id
|
||||||
|
* @GeneratedValue(strategy="AUTO")
|
||||||
|
*/
|
||||||
|
public $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string $title
|
||||||
|
*
|
||||||
|
* @Column(name="title", type="string", length=255)
|
||||||
|
*/
|
||||||
|
public $title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ManyToMany(targetEntity="DDC3033User")
|
||||||
|
* @JoinTable(
|
||||||
|
* name="user_purchases_3033",
|
||||||
|
* joinColumns={@JoinColumn(name="product_id", referencedColumnName="id")},
|
||||||
|
* inverseJoinColumns={@JoinColumn(name="user_id", referencedColumnName="id")}
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
public $buyers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->buyers = new ArrayCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @PreUpdate
|
||||||
|
*/
|
||||||
|
public function preUpdate(LifecycleEventArgs $eventArgs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @PostUpdate
|
||||||
|
*/
|
||||||
|
public function postUpdate(LifecycleEventArgs $eventArgs)
|
||||||
|
{
|
||||||
|
$em = $eventArgs->getEntityManager();
|
||||||
|
$uow = $em->getUnitOfWork();
|
||||||
|
$entity = $eventArgs->getEntity();
|
||||||
|
$classMetadata = $em->getClassMetadata(get_class($entity));
|
||||||
|
|
||||||
|
$uow->computeChangeSet($classMetadata, $entity);
|
||||||
|
$this->changeSet = $uow->getEntityChangeSet($entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Table
|
||||||
|
* @Entity @HasLifecycleCallbacks
|
||||||
|
*/
|
||||||
|
class DDC3033User
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var integer
|
||||||
|
*
|
||||||
|
* @Column(name="id", type="integer")
|
||||||
|
* @Id
|
||||||
|
* @GeneratedValue(strategy="AUTO")
|
||||||
|
*/
|
||||||
|
public $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*
|
||||||
|
* @Column(name="title", type="string", length=255)
|
||||||
|
*/
|
||||||
|
public $name;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user