entity listeners mapping
This commit is contained in:
parent
3d1956d260
commit
368cf73f89
@ -156,6 +156,10 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
|||||||
$this->addInheritedSqlResultSetMappings($class, $parent);
|
$this->addInheritedSqlResultSetMappings($class, $parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($parent && !empty($parent->entityListeners) && empty($class->entityListeners)) {
|
||||||
|
$class->entityListeners = $parent->entityListeners;
|
||||||
|
}
|
||||||
|
|
||||||
$class->setParentClasses($nonSuperclassParents);
|
$class->setParentClasses($nonSuperclassParents);
|
||||||
|
|
||||||
if ( $class->isRootEntity() && ! $class->isInheritanceTypeNone() && ! $class->discriminatorMap) {
|
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
|
* Completes the ID generator mapping. If "auto" is specified we choose the generator
|
||||||
* most appropriate for the targeted database platform.
|
* most appropriate for the targeted database platform.
|
||||||
*
|
*
|
||||||
|
@ -26,6 +26,7 @@ use Doctrine\DBAL\Types\Type;
|
|||||||
use ReflectionClass;
|
use ReflectionClass;
|
||||||
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
|
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
|
||||||
use Doctrine\Common\ClassLoader;
|
use Doctrine\Common\ClassLoader;
|
||||||
|
use Doctrine\Common\EventArgs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
|
* A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
|
||||||
@ -436,6 +437,18 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
*/
|
*/
|
||||||
public $lifecycleCallbacks = array();
|
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.
|
* READ-ONLY: The association mappings of this class.
|
||||||
*
|
*
|
||||||
@ -2492,6 +2505,41 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
$this->lifecycleCallbacks = $callbacks;
|
$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.
|
* Sets the discriminator column definition.
|
||||||
*
|
*
|
||||||
|
@ -25,6 +25,7 @@ use Doctrine\ORM\Mapping\JoinColumn;
|
|||||||
use Doctrine\ORM\Mapping\Column;
|
use Doctrine\ORM\Mapping\Column;
|
||||||
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
|
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
|
||||||
use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver;
|
use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver;
|
||||||
|
use Doctrine\ORM\Events;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The AnnotationDriver reads the mapping metadata from docblock annotations.
|
* 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
|
// Evaluate @HasLifecycleCallbacks annotation
|
||||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\HasLifecycleCallbacks'])) {
|
if (isset($classAnnotations['Doctrine\ORM\Mapping\HasLifecycleCallbacks'])) {
|
||||||
/* @var $method \ReflectionMethod */
|
/* @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.
|
// filter for the declaring class only, callbacks from parents will already be registered.
|
||||||
if ($method->isPublic() && $method->getDeclaringClass()->getName() == $class->name) {
|
if ($method->getDeclaringClass()->name !== $class->name) {
|
||||||
$annotations = $this->reader->getMethodAnnotations($method);
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if ($annotations) {
|
foreach ($this->getMethodCallbacks($method) as $value) {
|
||||||
foreach ($annotations as $key => $annot) {
|
list($callback, $event) = $value;
|
||||||
if ( ! is_numeric($key)) {
|
$metadata->addLifecycleCallback($callback, $event);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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
|
* @return array
|
||||||
*/
|
*/
|
||||||
|
@ -64,3 +64,4 @@ require_once __DIR__.'/../AssociationOverride.php';
|
|||||||
require_once __DIR__.'/../AssociationOverrides.php';
|
require_once __DIR__.'/../AssociationOverrides.php';
|
||||||
require_once __DIR__.'/../AttributeOverride.php';
|
require_once __DIR__.'/../AttributeOverride.php';
|
||||||
require_once __DIR__.'/../AttributeOverrides.php';
|
require_once __DIR__.'/../AttributeOverrides.php';
|
||||||
|
require_once __DIR__.'/../EntityListeners.php';
|
||||||
|
41
lib/Doctrine/ORM/Mapping/EntityListeners.php
Normal file
41
lib/Doctrine/ORM/Mapping/EntityListeners.php
Normal 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;
|
||||||
|
}
|
@ -7,6 +7,7 @@ namespace Doctrine\Tests\Models\Company;
|
|||||||
* @Table(name="company_contracts")
|
* @Table(name="company_contracts")
|
||||||
* @InheritanceType("SINGLE_TABLE")
|
* @InheritanceType("SINGLE_TABLE")
|
||||||
* @DiscriminatorColumn(name="discr", type="string")
|
* @DiscriminatorColumn(name="discr", type="string")
|
||||||
|
* @EntityListeners({"Doctrine\Tests\Models\Company\ContractSubscriber"})
|
||||||
* @DiscriminatorMap({
|
* @DiscriminatorMap({
|
||||||
* "fix" = "CompanyFixContract",
|
* "fix" = "CompanyFixContract",
|
||||||
* "flexible" = "CompanyFlexContract",
|
* "flexible" = "CompanyFlexContract",
|
||||||
|
31
tests/Doctrine/Tests/Models/Company/ContractSubscriber.php
Normal file
31
tests/Doctrine/Tests/Models/Company/ContractSubscriber.php
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user