Skip to content
This repository has been archived by the owner on Sep 23, 2022. It is now read-only.

Commit

Permalink
Use symfony dispatcher instead of doctrine one
Browse files Browse the repository at this point in the history
Using doctrine event manager to dispatch domain events prevents domain
event listeners to depend on doctrine (circular reference). Plus, it
doesn't make much more sense to dispatch domain events through a storage
layer dispatcher.

This commit allows to dispatch domain events through an
event_dispatcher (it uses the symfony event dispatcher by default).
  • Loading branch information
gquemener committed Sep 21, 2014
1 parent 963a94e commit f732bb5
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 8 deletions.
13 changes: 10 additions & 3 deletions DomainEvent/Dispatcher/Doctrine.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Knp\RadBundle\DomainEvent\Dispatcher;

use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Doctrine\ORM\EntityManager;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\PostFlushEventArgs;
Expand All @@ -10,12 +11,19 @@
/**
* Uses doctrine postFlush event,
* to loop over all entities that implement the "Provider" interface,
* and dipatches all the events using doctrine event manager.
* and dipatches all the events using a symfony event dispatcher
* It's important to use postFlush to ensure everything is saved correctly (transaction commited)
* before doing extra stuff (like sending emails f.e).
**/
class Doctrine implements EventSubscriber
{
protected $dispatcher;

public function __construct(EventDispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
}

public function getSubscribedEvents()
{
return array(
Expand All @@ -26,7 +34,6 @@ public function getSubscribedEvents()
public function postFlush(PostFlushEventArgs $event)
{
$em = $event->getEntityManager();
$evm = $em->getEventManager();
$identityMap = $em->getUnitOfWork()->getIdentityMap();

foreach ($identityMap as $class => $entities) {
Expand All @@ -37,7 +44,7 @@ public function postFlush(PostFlushEventArgs $event)

foreach ($entity->popEvents() as $event) {
$event->setSubject($entity);
$evm->dispatchEvent('on'.$event->getName(), $event);
$this->dispatcher->dispatch($event->getName(), $event);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions DomainEvent/Event.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

namespace Knp\RadBundle\DomainEvent;

use Doctrine\Common\EventArgs;
use Symfony\Component\EventDispatcher\Event as BaseEvent;

class Event extends EventArgs
class Event extends BaseEvent
{
private $eventName;
private $properties;
Expand Down
10 changes: 9 additions & 1 deletion DomainEvent/Provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,17 @@
interface Provider
{
/**
* empties and returns the list of events raised internally
* Empties and returns the list of events raised internally
*
* @return array
**/
public function popEvents();

/**
* Raise a domain event
*
* @param string $eventName
* @param array $properties
*/
public function raise($eventName, array $properties = array());
}
5 changes: 5 additions & 0 deletions DomainEvent/ProviderTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,9 @@ public function popEvents()

return $events;
}

public function raise($eventName, array $properties = array())
{
$this->events[] = new Event($eventName, $properties);
}
}
1 change: 1 addition & 0 deletions Resources/config/domain_event.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<services>

<service id="knp_rad.domain_event.dispatcher.doctrine" class="%knp_rad.domain_event.dispatcher.doctrine.class%">
<argument type="service" id="event_dispatcher" />
<tag name="doctrine.event_subscriber" />
<tag name="remove-when-missing" service="doctrine" />
</service>
Expand Down
56 changes: 56 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
Upgrade from 2.6 to 2.7
=======================

Domain Events
-------------

Doctrine listener does not dispatch domain events through doctrine event manager
anymore. It now does it through a Symfony event dispatcher. The one configured
by default is the `event_dispatcher` service (Yes, it means that you have access
to the whole event dispatcher symfony integration, like the debug toolbar).

You must change your domain event listeners DIC definition:

Before:
```yaml
services:
app.sync_listener:
class: App\SyncListener
tags:
- { name: doctrine.event_listener, event: onUserActivated }
```
After:
```yaml
services:
app.sync_listener:
class: App\SyncListener
tags:
- { name: kernel.event_listener, event: UserActivated }
```
Delayed event listeners definition must also be updated:
Before:
```yaml
parameters:
knp_rad.domain_event.delayed_event_names: [UserActivated]

services:
app.async_listener:
class: App\AsyncListener
tags:
- { name: doctrine.event_listener, event: onDelayedUserActivated }
```
After:
```yaml
parameters:
knp_rad.domain_event.delayed_event_names: [UserActivated]

services:
app.async_listener:
class: App\AsyncListener
tags:
- { name: kernel.event_listener, event: UserActivated, method: onDelayedUserActivated }
```
4 changes: 2 additions & 2 deletions features/domain_events.feature
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ Feature: Domain events
app.sync_listener:
class: App\SyncListener
tags:
- { name: doctrine.event_listener, event: onUserActivated }
- { name: kernel.event_listener, event: UserActivated }
"""
And I write in "App/Resources/config/rad.yml":
"""
Expand Down Expand Up @@ -106,7 +106,7 @@ Feature: Domain events
app.async_listener:
class: App\AsyncListener
tags:
- { name: doctrine.event_listener, event: onDelayedUserActivated }
- { name: kernel.event_listener, event: UserActivated, method: onDelayedUserActivated }
"""
When I visit "app_user_new" page
Then the file "App/UserActivated" should contain "1"
56 changes: 56 additions & 0 deletions spec/Knp/RadBundle/DomainEvent/Dispatcher/DoctrineSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace spec\Knp\RadBundle\DomainEvent\Dispatcher;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Doctrine\ORM\Event\PostFlushEventArgs;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\UnitOfWork;
use Knp\RadBundle\DomainEvent\Provider;
use Knp\RadBundle\DomainEvent\Event;

class DoctrineSpec extends ObjectBehavior
{
function let(EventDispatcherInterface $dispatcher)
{
$this->beConstructedWith($dispatcher);
}

function it_is_a_doctrine_event_subscriber()
{
$this->shouldBeAnInstanceOf('Doctrine\Common\EventSubscriber');
}

function it_subscribes_to_the_postFlush_event()
{
$this->getSubscribedEvents()->shouldReturn(array('postFlush'));
}

function it_dispatches_domain_events_after_doctrine_unit_of_work_has_been_flushed(
PostFlushEventArgs $event,
EntityManager $em,
UnitOfWork $uow,
Provider $entity,
Event $event1,
Event $event2,
$dispatcher
) {
$event->getEntityManager()->willReturn($em);
$em->getUnitOfWork()->willReturn($uow);
$uow->getIdentityMap()->willReturn([[
$entity
]]);
$entity->popEvents()->willReturn([$event1, $event2]);
$event1->getName()->willReturn('EntityCreated');
$event2->getName()->willReturn('PropertyUpdated');

$event1->setSubject($entity)->shouldBeCalled();
$event2->setSubject($entity)->shouldBeCalled();
$dispatcher->dispatch('EntityCreated', $event1)->shouldBeCalled();
$dispatcher->dispatch('PropertyUpdated', $event2)->shouldBeCalled();

$this->postFlush($event);
}
}

0 comments on commit f732bb5

Please sign in to comment.