Skip to content
New issue

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

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[doc] adding some more doc and examples for lifecycle event listeners and subscribers #621

Merged
merged 2 commits into from
Apr 6, 2013
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 120 additions & 49 deletions docs/en/reference/events.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ Events
======

Doctrine 2 features a lightweight event system that is part of the
Common package.
Common package. Doctrine uses it to dispatch system events, mainly
:ref:`lifecycle events <reference-events-lifecycle-events>`.
You can also use it for your own custom events.

The Event System
----------------
Expand All @@ -27,28 +29,28 @@ Now we can add some event listeners to the ``$evm``. Let's create a
{
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);

Expand Down Expand Up @@ -80,22 +82,30 @@ array of events it should be subscribed to.
class TestEventSubscriber implements \Doctrine\Common\EventSubscriber
{
public $preFooInvoked = false;

public function preFoo()
{
$this->preFooInvoked = true;
}

public function getSubscribedEvents()
{
return array(TestEvent::preFoo);
}
}

$eventSubscriber = new TestEventSubscriber();
$evm->addEventSubscriber($eventSubscriber);

Now when you dispatch an event any event subscribers will be
.. note::

If you are familiar with the Symfony2 event manager, note that
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you rewrite this to remove the event manager of sf2 comparison? While it's a good example, stating that it's just an array of callback method names is perfectly fine.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simplified that.

i pushed the changes for the comments.

the array returned by getSubscribedEvents is different. For
doctrine, the array values must be the event names, and the
names are used as method names that will be called when the
event occurs.

Now when you dispatch an event, any event subscribers will be
notified for that event.

.. code-block:: php
Expand Down Expand Up @@ -133,6 +143,8 @@ several reasons:
An example for a correct notation can be found in the example
``EventTest`` above.

.. _reference-events-lifecycle-events:

Lifecycle Events
----------------

Expand Down Expand Up @@ -164,7 +176,7 @@ the life-time of their registered entities.
- loadClassMetadata - The loadClassMetadata event occurs after the
mapping metadata for a class has been loaded from a mapping source
(annotations/xml/yaml).
- preFlush - The preFlush event occurs at the very beginning of a flush
- preFlush - The preFlush event occurs at the very beginning of a flush
operation. This event is not a lifecycle callback.
- onFlush - The onFlush event occurs after the change-sets of all
managed entities are computed. This event is not a lifecycle
Expand Down Expand Up @@ -198,7 +210,7 @@ listeners:

- Lifecycle Callbacks are methods on the entity classes that are
called when the event is triggered. They receives some kind of ``EventArgs``.
- Lifecycle Event Listeners are classes with specific callback
- Lifecycle Event Listeners and Subscribers are classes with specific callback
methods that receives some kind of ``EventArgs`` instance which
give access to the entity, EntityManager or other relevant data.

Expand All @@ -222,44 +234,44 @@ event occurs.
.. code-block:: php

<?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:i: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()
{
Expand All @@ -283,28 +295,28 @@ can do it with the following.
type: string(50)
lifecycleCallbacks:
prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ]
postPersist: [ doStuffOnPostPersist ]
postPersist: [ doStuffOnPostPersist ]

XML would look something like this:

.. code-block:: 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
Expand All @@ -315,16 +327,16 @@ model.

<?php
// ...

class User
{
// ...

public function doStuffOnPrePersist()
{
// ...
}

public function doStuffOnPostPersist()
{
// ...
Expand Down Expand Up @@ -360,8 +372,8 @@ With the additional argument you have access to the
}
}

Listening to Lifecycle Events
-----------------------------
Listening and subscribing to Lifecycle Events
---------------------------------------------

Lifecycle event listeners are much more powerful than the simple
lifecycle callbacks that are defined on the entity classes. They
Expand All @@ -371,7 +383,66 @@ 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
For event subscribers, there are no surprises. They declare the
lifecycle events in their ``getSubscribedEvents`` method and provide
public methods that expect the relevant arguments.

A lifecycle event listener looks like the following:

.. code-block:: php

<?php
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;

class MyEventListener
{
public function preUpdate(LifecycleEventArgs $args)
{
$entity = $args->getObject();
$entityManager = $args->getObjectManager();

// perhaps you only want to act on some "Product" entity
if ($entity instanceof Product) {
// do something with the Product
}
}
}

A lifecycle event subscriber may looks like this:

.. code-block:: php

<?php
use Doctrine\Common\EventSubscriber;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;

class MyEventSubscriber implements EventSubscriber
{
public function getSubscribedEvents()
{
return array(
'postUpdate',
);
}

public function postUpdate(LifecycleEventArgs $args)
{
$entity = $args->getObject();
$entityManager = $args->getObjectManager();

// perhaps you only want to act on some "Product" entity
if ($entity instanceof Product) {
// do something with the Product
}
}

.. note::

Lifecycle events are triggered for all entities. It is the responsibility
of the listeners and subscribers to check if the entity is of a type
it wants to handle.

To register an event listener or subscriber, you have to hook it into the
EventManager that is passed to the EntityManager factory:

.. code-block:: php
Expand All @@ -380,7 +451,7 @@ EventManager that is passed to the EntityManager factory:
$eventManager = new EventManager();
$eventManager->addEventListener(array(Events::preUpdate), new MyEventListener());
$eventManager->addEventSubscriber(new MyEventSubscriber());

$entityManager = EntityManager::create($dbOpts, $config, $eventManager);

You can also retrieve the event manager instance after the
Expand Down Expand Up @@ -451,8 +522,8 @@ called during a flush operation.
preFlush
~~~~~~~~

``preFlush`` is called at ``EntityManager#flush()`` before
anything else. ``EntityManager#flush()`` can be called safely
``preFlush`` is called at ``EntityManager#flush()`` before
anything else. ``EntityManager#flush()`` can be called safely
inside its listeners.

.. code-block:: php
Expand Down Expand Up @@ -497,25 +568,25 @@ mentioned sets. See this example:
{
$em = $eventArgs->getEntityManager();
$uow = $em->getUnitOfWork();

foreach ($uow->getScheduledEntityInsertions() AS $entity) {

}

foreach ($uow->getScheduledEntityUpdates() AS $entity) {

}

foreach ($uow->getScheduledEntityDeletions() AS $entity) {

}

foreach ($uow->getScheduledCollectionDeletions() AS $col) {

}

foreach ($uow->getScheduledCollectionUpdates() AS $col) {

}
}
}
Expand Down Expand Up @@ -615,7 +686,7 @@ lifecycle callback when there are expensive validations to call:
}
}
}

private function validateCreditCard($no)
{
// throw an exception to interrupt flush event. Transaction will be rolled back.
Expand Down Expand Up @@ -788,7 +859,7 @@ you should map the listener method using the event type mapping.
postRemove: [postRemoveHandler]
preRemove: [preRemoveHandler]
# ....



Entity listeners resolver
Expand Down Expand Up @@ -867,7 +938,7 @@ process and manipulate the instance.
$metadataFactory = $em->getMetadataFactory();
$evm = $em->getEventManager();
$evm->addEventListener(Events::loadClassMetadata, $test);

class EventTest
{
public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs)
Expand Down