1
0
mirror of synced 2024-12-05 03:06:05 +03:00

Event listener to programmatically attach entity listeners.

This commit is contained in:
fabios 2013-11-18 15:03:00 -05:00
parent 3529cd4282
commit e69cd37226
4 changed files with 284 additions and 5 deletions

View File

@ -2510,7 +2510,11 @@ class ClassMetadataInfo implements ClassMetadata
*/
public function addEntityListener($eventName, $class, $method)
{
$class = $this->fullyQualifiedClassName($class);
$class = $this->fullyQualifiedClassName($class);
$listener = array(
'class' => $class,
'method' => $method
);
if ( ! class_exists($class)) {
throw MappingException::entityListenerClassNotFound($class, $this->name);
@ -2520,10 +2524,11 @@ class ClassMetadataInfo implements ClassMetadata
throw MappingException::entityListenerMethodNotFound($class, $method, $this->name);
}
$this->entityListeners[$eventName][] = array(
'class' => $class,
'method' => $method
);
if (isset($this->entityListeners[$eventName]) && in_array($listener, $this->entityListeners[$eventName])) {
throw MappingException::duplicateEntityListener($class, $method, $this->name);
}
$this->entityListeners[$eventName][] = $listener;
}
/**

View File

@ -707,6 +707,18 @@ class MappingException extends \Doctrine\ORM\ORMException
return new self(sprintf('Entity Listener "%s" declared on "%s" has no method "%s".', $listenerName, $className, $methodName));
}
/**
* @param string $listenerName
* @param string $methodName
* @param string $className
*
* @return \Doctrine\ORM\Mapping\MappingException
*/
public static function duplicateEntityListener($listenerName, $methodName, $className)
{
return new self(sprintf('Entity Listener "%s#%s()" in "%s" was already declared, but it must be declared only once.', $listenerName, $methodName, $className));
}
/**
* @param string $className
* @param string $annotation

View File

@ -0,0 +1,79 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
/**
* Mechanism to programmatically attach entity listeners.
*
* @author Fabio B. SIlva <fabio.bat.silva@gmail.com>
*
* @since 2.5
*/
class AttachEntityListenersListener
{
/**
* @var array[]
*/
private $entityListeners = array();
/**
* Adds a entity listener for a specific entity.
*
* @param string $entityClass The entity to attach the listener.
* @param string $listenerClass The listener class.
* @param string $eventName The entity lifecycle event.
* @param string $listenerCallback|null The listener callback method or NULL to use $eventName.
*
* @return void
*/
public function addEntityListener($entityClass, $listenerClass, $eventName, $listenerCallback = null)
{
$this->entityListeners[ltrim($entityClass, '\\')][] = array(
'event' => $eventName,
'class' => $listenerClass,
'method' => $listenerCallback ?: $eventName
);
}
/**
* Processes event and attach the entity listener.
*
* @param \Doctrine\ORM\Event\LoadClassMetadataEventArgs $event
*
* @return void
*/
public function loadClassMetadata(LoadClassMetadataEventArgs $event)
{
/** @var $metadata \Doctrine\ORM\Mapping\ClassMetadata */
$metadata = $event->getClassMetadata();
if ( ! isset($this->entityListeners[$metadata->name])) {
return;
}
foreach ($this->entityListeners[$metadata->name] as $listener) {
$metadata->addEntityListener($listener['event'], $listener['class'], $listener['method']);
}
unset($this->entityListeners[$metadata->name]);
}
}

View File

@ -0,0 +1,183 @@
<?php
namespace Doctrine\Tests\ORM\Tools;
use Doctrine\ORM\Events;
use Doctrine\Tests\OrmTestCase;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\Tools\AttachEntityListenersListener;
class AttachEntityListenersListenerTest extends OrmTestCase
{
/**
* @var \Doctrine\ORM\EntityManager
*/
private $em;
/**
* @var \Doctrine\ORM\Tools\AttachEntityListenersListener
*/
private $listener;
/**
* @var \Doctrine\ORM\Mapping\ClassMetadataFactory
*/
private $factory;
public function setUp()
{
$this->listener = new AttachEntityListenersListener();
$driver = $this->createAnnotationDriver();
$this->em = $this->_getTestEntityManager();
$evm = $this->em->getEventManager();
$this->factory = new ClassMetadataFactory;
$evm->addEventListener(Events::loadClassMetadata, $this->listener);
$this->em->getConfiguration()->setMetadataDriverImpl($driver);
$this->factory->setEntityManager($this->em);
}
public function testAttachEntityListeners()
{
$this->listener->addEntityListener(
AttachEntityListenersListenerTestFooEntity::CLASSNAME,
AttachEntityListenersListenerTestListener::CLASSNAME,
Events::postLoad,
'postLoadHandler'
);
$metadata = $this->factory->getMetadataFor(AttachEntityListenersListenerTestFooEntity::CLASSNAME);
$this->assertArrayHasKey('postLoad', $metadata->entityListeners);
$this->assertCount(1, $metadata->entityListeners['postLoad']);
$this->assertEquals('postLoadHandler', $metadata->entityListeners['postLoad'][0]['method']);
$this->assertEquals(AttachEntityListenersListenerTestListener::CLASSNAME, $metadata->entityListeners['postLoad'][0]['class']);
}
public function testAttachToExistingEntityListeners()
{
$this->listener->addEntityListener(
AttachEntityListenersListenerTestBarEntity::CLASSNAME,
AttachEntityListenersListenerTestListener2::CLASSNAME,
Events::prePersist
);
$this->listener->addEntityListener(
AttachEntityListenersListenerTestBarEntity::CLASSNAME,
AttachEntityListenersListenerTestListener2::CLASSNAME,
Events::postPersist,
'postPersistHandler'
);
$metadata = $this->factory->getMetadataFor(AttachEntityListenersListenerTestBarEntity::CLASSNAME);
$this->assertArrayHasKey('postPersist', $metadata->entityListeners);
$this->assertArrayHasKey('prePersist', $metadata->entityListeners);
$this->assertCount(2, $metadata->entityListeners['prePersist']);
$this->assertCount(2, $metadata->entityListeners['postPersist']);
$this->assertEquals('prePersist', $metadata->entityListeners['prePersist'][0]['method']);
$this->assertEquals(AttachEntityListenersListenerTestListener::CLASSNAME, $metadata->entityListeners['prePersist'][0]['class']);
$this->assertEquals('prePersist', $metadata->entityListeners['prePersist'][1]['method']);
$this->assertEquals(AttachEntityListenersListenerTestListener2::CLASSNAME, $metadata->entityListeners['prePersist'][1]['class']);
$this->assertEquals('postPersist', $metadata->entityListeners['postPersist'][0]['method']);
$this->assertEquals(AttachEntityListenersListenerTestListener::CLASSNAME, $metadata->entityListeners['postPersist'][0]['class']);
$this->assertEquals('postPersistHandler', $metadata->entityListeners['postPersist'][1]['method']);
$this->assertEquals(AttachEntityListenersListenerTestListener2::CLASSNAME, $metadata->entityListeners['postPersist'][1]['class']);
}
/**
* @expectedException \Doctrine\ORM\Mapping\MappingException
* @expectedExceptionMessage Entity Listener "Doctrine\Tests\ORM\Tools\AttachEntityListenersListenerTestListener#postPersist()" in "Doctrine\Tests\ORM\Tools\AttachEntityListenersListenerTestFooEntity" was already declared, but it must be declared only once.
*/
public function testDuplicateEntityListenerException()
{
$this->listener->addEntityListener(
AttachEntityListenersListenerTestFooEntity::CLASSNAME,
AttachEntityListenersListenerTestListener::CLASSNAME,
Events::postPersist
);
$this->listener->addEntityListener(
AttachEntityListenersListenerTestFooEntity::CLASSNAME,
AttachEntityListenersListenerTestListener::CLASSNAME,
Events::postPersist
);
$this->factory->getMetadataFor(AttachEntityListenersListenerTestFooEntity::CLASSNAME);
}
}
/**
* @Entity
*/
class AttachEntityListenersListenerTestFooEntity
{
const CLASSNAME = __CLASS__;
/**
* @Id
* @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
public $id;
}
/**
* @Entity
* @EntityListeners({"AttachEntityListenersListenerTestListener"})
*/
class AttachEntityListenersListenerTestBarEntity
{
const CLASSNAME = __CLASS__;
/**
* @Id
* @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
public $id;
}
class AttachEntityListenersListenerTestListener
{
const CLASSNAME = __CLASS__;
public $calls;
public function prePersist()
{
$this->calls[__FUNCTION__][] = func_get_args();
}
public function postLoadHandler()
{
$this->calls[__FUNCTION__][] = func_get_args();
}
public function postPersist()
{
$this->calls[__FUNCTION__][] = func_get_args();
}
}
class AttachEntityListenersListenerTestListener2
{
const CLASSNAME = __CLASS__;
public $calls;
public function prePersist()
{
$this->calls[__FUNCTION__][] = func_get_args();
}
public function postPersistHandler()
{
$this->calls[__FUNCTION__][] = func_get_args();
}
}