1
0
mirror of synced 2025-01-31 04:21:44 +03:00

entity listeners mapping

This commit is contained in:
Fabio B. Silva 2012-07-29 19:10:33 -03:00 committed by fabio.silva
parent 3d1956d260
commit 368cf73f89
8 changed files with 247 additions and 46 deletions

View File

@ -156,6 +156,10 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
$this->addInheritedSqlResultSetMappings($class, $parent);
}
if ($parent && !empty($parent->entityListeners) && empty($class->entityListeners)) {
$class->entityListeners = $parent->entityListeners;
}
$class->setParentClasses($nonSuperclassParents);
if ( $class->isRootEntity() && ! $class->isInheritanceTypeNone() && ! $class->discriminatorMap) {
@ -411,7 +415,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
}
}
/**
/**
* Completes the ID generator mapping. If "auto" is specified we choose the generator
* most appropriate for the targeted database platform.
*

View File

@ -26,6 +26,7 @@ use Doctrine\DBAL\Types\Type;
use ReflectionClass;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use Doctrine\Common\ClassLoader;
use Doctrine\Common\EventArgs;
/**
* A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
@ -436,6 +437,18 @@ class ClassMetadataInfo implements ClassMetadata
*/
public $lifecycleCallbacks = array();
/**
* READ-ONLY: The registered entity listeners.
*
* @var array
*/
public $entityListeners = array();
/**
* @var array entity listeners instances.
*/
static private $entityListenerInstances = array();
/**
* READ-ONLY: The association mappings of this class.
*
@ -2492,6 +2505,41 @@ class ClassMetadataInfo implements ClassMetadata
$this->lifecycleCallbacks = $callbacks;
}
/**
* Adds a entity listener for entities of this class.
*
* @param string $callback
* @param string $eventName
*/
public function addEntityListener($eventName, $class, $method)
{
$this->entityListeners[$eventName][] = array(
'class' => $class,
'method' => $method
);
}
/**
* Call the entity listeners.
*
* @param string $eventName The event name.
* @param object $entity An instance of the mapped entity
* @param \Doctrine\Common\EventArgs $arg The Event args
*/
public function dispatchEntityListeners($eventName, $entity, EventArgs $arg)
{
foreach ($this->entityListeners[$eventName] as $listener) {
$class = $listener['class'];
$method = $listener['method'];
if ( ! isset(self::$entityListenerInstances[$class])) {
self::$entityListenerInstances[$class] = new $class();
}
self::$entityListenerInstances[$class]->{$method}($entity, $arg);
}
}
/**
* Sets the discriminator column definition.
*

View File

@ -25,6 +25,7 @@ use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\Column;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver;
use Doctrine\ORM\Events;
/**
* The AnnotationDriver reads the mapping metadata from docblock annotations.
@ -415,54 +416,39 @@ class AnnotationDriver extends AbstractAnnotationDriver
}
}
// Evaluate EntityListeners annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\EntityListeners'])) {
$entityListenersAnnot = $classAnnotations['Doctrine\ORM\Mapping\EntityListeners'];
foreach ($entityListenersAnnot->value as $listener) {
if ( ! class_exists($listener)) {
throw new \InvalidArgumentException("Indefined class \"$listener\"");
}
$listener = new \ReflectionClass($listener);
foreach ($listener->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
foreach ($this->getMethodCallbacks($method) as $value) {
list($callback, $event) = $value;
$metadata->addEntityListener($event, $listener->name, $callback);
}
}
}
}
// Evaluate @HasLifecycleCallbacks annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\HasLifecycleCallbacks'])) {
/* @var $method \ReflectionMethod */
foreach ($class->getMethods() as $method) {
foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
// filter for the declaring class only, callbacks from parents will already be registered.
if ($method->isPublic() && $method->getDeclaringClass()->getName() == $class->name) {
$annotations = $this->reader->getMethodAnnotations($method);
if ($method->getDeclaringClass()->name !== $class->name) {
continue;
}
if ($annotations) {
foreach ($annotations as $key => $annot) {
if ( ! is_numeric($key)) {
continue;
}
$annotations[get_class($annot)] = $annot;
}
}
if (isset($annotations['Doctrine\ORM\Mapping\PrePersist'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::prePersist);
}
if (isset($annotations['Doctrine\ORM\Mapping\PostPersist'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postPersist);
}
if (isset($annotations['Doctrine\ORM\Mapping\PreUpdate'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::preUpdate);
}
if (isset($annotations['Doctrine\ORM\Mapping\PostUpdate'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postUpdate);
}
if (isset($annotations['Doctrine\ORM\Mapping\PreRemove'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::preRemove);
}
if (isset($annotations['Doctrine\ORM\Mapping\PostRemove'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postRemove);
}
if (isset($annotations['Doctrine\ORM\Mapping\PostLoad'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postLoad);
}
if (isset($annotations['Doctrine\ORM\Mapping\PreFlush'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::preFlush);
}
foreach ($this->getMethodCallbacks($method) as $value) {
list($callback, $event) = $value;
$metadata->addLifecycleCallback($callback, $event);
}
}
}
@ -488,9 +474,55 @@ class AnnotationDriver extends AbstractAnnotationDriver
}
/**
* Parses the given JoinColumn as array.
* Parses the given method.
*
* @param JoinColumn $joinColumn
* @param \ReflectionMethod $method
* @return array
*/
private function getMethodCallbacks(\ReflectionMethod $method)
{
$callbacks = array();
$annotations = $this->reader->getMethodAnnotations($method);
foreach ($annotations as $annot) {
if ($annot instanceof \Doctrine\ORM\Mapping\PrePersist) {
$callbacks[] = array($method->name, Events::prePersist);
}
if ($annot instanceof \Doctrine\ORM\Mapping\PostPersist) {
$callbacks[] = array($method->name, Events::postPersist);
}
if ($annot instanceof \Doctrine\ORM\Mapping\PreUpdate) {
$callbacks[] = array($method->name, Events::preUpdate);
}
if ($annot instanceof \Doctrine\ORM\Mapping\PostUpdate) {
$callbacks[] = array($method->name, Events::postUpdate);
}
if ($annot instanceof \Doctrine\ORM\Mapping\PreRemove) {
$callbacks[] = array($method->name, Events::preRemove);
}
if ($annot instanceof \Doctrine\ORM\Mapping\PostRemove) {
$callbacks[] = array($method->name, Events::postRemove);
}
if ($annot instanceof \Doctrine\ORM\Mapping\PostLoad) {
$callbacks[] = array($method->name, Events::postLoad);
}
if ($annot instanceof \Doctrine\ORM\Mapping\PreFlush) {
$callbacks[] = array($method->name, Events::preFlush);
}
}
return $callbacks;
}
/**
* Parse the given JoinColumn as array
*
* @return array
*/

View File

@ -64,3 +64,4 @@ require_once __DIR__.'/../AssociationOverride.php';
require_once __DIR__.'/../AssociationOverrides.php';
require_once __DIR__.'/../AttributeOverride.php';
require_once __DIR__.'/../AttributeOverrides.php';
require_once __DIR__.'/../EntityListeners.php';

View File

@ -0,0 +1,41 @@
<?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\Mapping;
/**
* The EntityListeners annotation specifies the callback listener classes to be used for an entity or mapped superclass.
* The EntityListeners annotation may be applied to an entity class or mapped superclass.
*
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @since 2.4
*
* @Annotation
* @Target("CLASS")
*/
final class EntityListeners implements Annotation
{
/**
* Specifies the names of the entity listeners.
*
* @var array<string>
*/
public $value;
}

View File

@ -7,6 +7,7 @@ namespace Doctrine\Tests\Models\Company;
* @Table(name="company_contracts")
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(name="discr", type="string")
* @EntityListeners({"Doctrine\Tests\Models\Company\ContractSubscriber"})
* @DiscriminatorMap({
* "fix" = "CompanyFixContract",
* "flexible" = "CompanyFlexContract",

View File

@ -0,0 +1,31 @@
<?php
namespace Doctrine\Tests\Models\Company;
class ContractSubscriber
{
static public $prePersistCalls;
static public $postPersisCalls;
static public $instances;
public function __construct()
{
self::$instances[] = $this;
}
/**
* @PostPersist
*/
public function postPersist(CompanyContract $contract)
{
self::$postPersisCalls[] = func_get_args();
}
/**
* @PrePersist
*/
public function prePersist(CompanyContract $contract)
{
self::$prePersistCalls[] = func_get_args();
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\ORM\Events;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\Tests\Models\Company\CompanyFixContract;
use Doctrine\Tests\Models\Company\CompanyFlexContract;
use Doctrine\Tests\Models\Company\ContractSubscriber;
require_once __DIR__ . '/../../TestInit.php';
class EntityListenersDispatcherTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
/**
* @group DDC-1955
*/
public function testEntityListeners()
{
$flexClass = $this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyFixContract');
$fixClass = $this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyFlexContract');
$this->assertNull(ContractSubscriber::$instances);
$this->assertNull(ContractSubscriber::$prePersistCalls);
$this->assertNull(ContractSubscriber::$postPersisCalls);
$fix = new CompanyFixContract();
$fixArg = new LifecycleEventArgs($fix, $this->_em);
$flex = new CompanyFlexContract();
$flexArg = new LifecycleEventArgs($fix, $this->_em);
$fixClass->dispatchEntityListeners(Events::prePersist, $fix, $fixArg);
$flexClass->dispatchEntityListeners(Events::prePersist, $flex, $flexArg);
$this->assertSame($fix, ContractSubscriber::$prePersistCalls[0][0]);
$this->assertSame($fixArg, ContractSubscriber::$prePersistCalls[0][1]);
$this->assertCount(1, ContractSubscriber::$instances);
$this->assertNull(ContractSubscriber::$postPersisCalls);
}
}