1
0
mirror of synced 2024-12-16 16:16:04 +03:00
doctrine2/manual/en/events.txt
2010-04-23 12:16:31 -04:00

305 lines
10 KiB
Plaintext

Doctrine 2 features a lightweight event system that is part of the Common package.
++ The Event System
The event system is controlled by the `EventManager`. It is the central point
of Doctrine's event listener system. Listeners are registered on the manager
and events are dispatched through the manager.
[php]
$evm = new EventManager();
Now we can add some event listeners to the `$evm`. Let's create a `EventTest` class
to play around with.
[php]
class EventTest
{
const preFoo = 'preFoo';
const postFoo = 'postFoo';
private $_evm;
public $preFooInvoked = false;
public $postFooInvoked = false;
public function __construct($evm)
{
$evm->addEventListener(array(self::preFoo, self::postFoo), $this);
}
public function preFoo(EventArgs $e)
{
$this->preFooInvoked = true;
}
public function postFoo(EventArgs $e)
{
$this->postFooInvoked = true;
}
}
// Create a new instance
$test = new EventTest($evm);
Events can be dispatched by using the `dispatchEvent()` method.
[php]
$evm->dispatchEvent(EventTest::preFoo);
$evm->dispatchEvent(EventTest::postFoo);
You can easily remove a listener with the `removeEventListener()` method.
[php]
$evm->removeEventListener(array(self::preFoo, self::postFoo), $this);
The Doctrine 2 event system also has a simple concept of event subscribers. We
can define a simple `TestEventSubscriber` class which implements the
`\Doctrine\Common\EventSubscriber` interface and implements a `getSubscribedEvents()`
method which returns an array of events it should be subscribed to.
[php]
class TestEventSubscriber implements \Doctrine\Common\EventSubscriber
{
const preFoo = 'preFoo';
public $preFooInvoked = false;
public function preFoo()
{
$this->preFooInvoked = true;
}
public function getSubscribedEvents()
{
return array(self::preFoo);
}
}
$eventSubscriber = new TestEventSubscriber();
$evm->addEventSubscriber($eventSubscriber);
Now when you dispatch an event any event subscribers will be notified for that event.
[php]
$evm->dispatchEvent(TestEventSubscriber::preFoo);
Now the test the `$eventSubscriber` instance to see if the `preFoo()` method was invoked.
[php]
if ($eventSubscriber->preFooInvoked) {
echo 'pre foo invoked!';
}
++ Lifecycle Events
The EntityManager and UnitOfWork trigger a bunch of events during the life-time of their registered entities.
* preRemove - The preRemove event occurs for a given entity before the respective EntityManager remove operation for that entity is executed.
* postRemove - The postRemove event occurs for an entity after the entity has been deleted. It will be invoked after the database delete operations.
* prePersist - The prePersist event occurs for a given entity before the respective EntityManager persist operation for that entity is executed.
* postPersist - The postPersist event occurs for an entity after the entity has been made persistent. It will be invoked after the database insert operations. Generated primary key values are available in the postPersist event.
* preUpdate - The preUpdate event occurs before the database update operations to entity data.
* postUpdate - The postUpdate event occurs after the database update operations to entity data.
* postLoad - The postLoad event occurs for an entity after the entity has been loaded into the current EntityManager from the database or after the refresh operation has been applied to it.
* loadClassMetadata - The loadClassMetadata event occurs after the mapping metadata for a class has been loaded from a mapping source (annotations/xml/yaml).
* onFlush - The onFlush event occours after the change-sets of all managed entities are computed. This event is not a lifecycle callback.
> **CAUTION**
> Note that the postLoad event occurs for an entity before any associations have been
> initialized. Therefore it is not safe to access associations in a postLoad callback
> or event handler.
You can access the Event constants from the `Events` class in the ORM package.
[php]
use Doctrine\ORM\Events;
echo Events::preUpdate;
These can be hooked into by two different types of event listeners:
* Lifecycle Callbacks are methods on the entity classes that are called when the event is triggered. They recieve absolutely no arguments and are specifically designed to allow changes inside the entity classes state.
* Lifecycle Event Listeners are classes with specific callback methods that recieves some kind of `EventArgs` instance which give access to the entity, EntityManager or other relevant data.
> **NOTE**
> All Lifecycle events that happen during the `flush()` of an EntityManager have very specific constraints on the allowed
> operations that can be executed. Please read the *Implementing Event Listeners* section very carefully to understand
> which operations are allowed in which lifecycle event.
++ Lifecycle Callbacks
A lifecycle event is a regular event with the additional feature of providing
a mechanism to register direct callbacks inside the corresponding entity classes
that are executed when the lifecycle event occurs.
[php]
/** @Entity @HasLifecycleCallbacks */
class User
{
// ...
/**
* @Column(type="string", length=255)
*/
public $value;
/** @Column(name="created_at", type="string", length=255) */
private $createdAt;
/** @PrePersist */
public function doStuffOnPrePersist()
{
$this->createdAt = date('Y-m-d H:m:s');
}
/** @PrePersist */
public function doOtherStuffOnPrePersist()
{
$this->value = 'changed from prePersist callback!';
}
/** @PostPersist */
public function doStuffOnPostPersist()
{
$this->value = 'changed from postPersist callback!';
}
/** @PostLoad */
public function doStuffOnPostLoad()
{
$this->value = 'changed from postLoad callback!';
}
/** @PreUpdate */
public function doStuffOnPreUpdate()
{
$this->value = 'changed from preUpdate callback!';
}
}
Note that when using 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 can do it with
the following.
[yml]
User:
type: entity
fields:
# ...
name:
type: string(50)
lifecycleCallbacks:
doStuffOnPrePersist: prePersist
doStuffOnPostPersist: postPersist
XML would look something like this:
[xml]
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
/Users/robo/dev/php/Doctrine/doctrine-mapping.xsd">
<entity name="User">
<lifecycle-callbacks>
<lifecycle-callback type="prePersist" method="doStuffOnPrePersist"/>
<lifecycle-callback type="postPersist" method="doStuffOnPostPersist"/>
</lifecycle-callbacks>
</entity>
</doctrine-mapping>
You just need to make sure a public `doStuffOnPrePersist()` and `doStuffOnPostPersist()` method is defined on your `User` model.
[php]
// ...
class User
{
// ...
public function doStuffOnPrePersist()
{
// ...
}
public function doStuffOnPostPersist()
{
// ...
}
}
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.
++ Listening to Lifecycle Events
Lifecycle event listeners are much more powerful than the simple lifecycle callbacks that are defined on the entity
classes. They allow to implement re-usable behaviours between different entity classes, yet require much more detailed
knowledge about the inner workings of the EntityManager and UnitOfWork. Please read the *Implementing Event Listeners*
section carefully if you are trying to write your own listener.
To register an event listener you have to hook it into the EventManager that is passed to the EntityManager factory:
[php]
$eventManager = new EventManager();
$eventManager->addEventListener(array(Events::preUpdate), MyEventListener());
$eventManager->addEventSubscriber(new MyEventSubscriber());
$entityManager = EntityManager::create($dbOpts, $config, $eventManager);
You can also retrieve the event manager instance after the EntityManager was created:
[php]
$entityManager->getEventManager()->addEventListener(array(Events::preUpdate), MyEventListener());
$entityManager->getEventManager()->addEventSubscriber(new MyEventSubscriber());
++ Implementing Event Listeners
This section explains what is and what is not allowed during specific lifecycle events of the UnitOfWork.
Although you get passed the EntityManager in all of these events, you have to follow this restrictions very
carefully since operations in the wrong event may produce lots of different errors, such as inconsistent data and
lost updates/persists/removes.
+++ prePersist
+++ preRemove
+++ onFlush
+++ preUpdate
+++ postUpdate, postRemove, postPersist
++ Load ClassMetadata Event
When the mapping information for an entity is read, it is populated in to a
`ClassMetadataInfo` instance. You can hook in to this process and manipulate
the instance.
[php]
$test = new EventTest();
$metadataFactory = $em->getMetadataFactory();
$evm = $em->getEventManager();
$evm->addEventListener(Events::loadClassMetadata, $test);
class EventTest
{
public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs)
{
$classMetadata = $eventArgs->getClassMetadata();
$fieldMapping = array(
'fieldName' => 'about',
'type' => 'string',
'length' => 255
);
$classMetadata->mapField($fieldMapping);
}
}