diff --git a/docs/en/reference/events.rst b/docs/en/reference/events.rst
index a6b75ad12..d921b42a1 100644
--- a/docs/en/reference/events.rst
+++ b/docs/en/reference/events.rst
@@ -196,9 +196,7 @@ listeners:
- Lifecycle Callbacks are methods on the entity classes that are
- called when the event is triggered. They receive absolutely no
- arguments and are specifically designed to allow changes inside the
- entity classes state.
+ called when the event is triggered. They receives some kind of ``EventArgs``.
- Lifecycle Event Listeners are classes with specific callback
methods that receives some kind of ``EventArgs`` instance which
give access to the entity, EntityManager or other relevant data.
@@ -336,6 +334,31 @@ The ``key`` of the lifecycleCallbacks is the name of the method and
the value is the event type. The allowed event types are the ones
listed in the previous Lifecycle Events section.
+.. versionadded:: 2.4
+
+Lifecycle Callbacks Event Argument
+-----------------------------------
+
+Since 2.4 the triggered event is given to the lifecycle-callback.
+
+With the additional argument you have access to the
+``EntityManager`` and ``UnitOfWork`` APIs inside these callback methods.
+
+.. code-block:: php
+
+ hasChangedField('username')) {
+ // Do something when the username is changed.
+ }
+ }
+ }
+
Listening to Lifecycle Events
-----------------------------
@@ -626,6 +649,207 @@ postLoad
This event is called after an entity is constructed by the
EntityManager.
+Entity listeners
+----------------
+
+An entity listeners is a lifecycle listener classes used for an entity.
+
+- The entity listeners mapping may be applied to an entity class or mapped superclass.
+- An entity listener is defined by mapping the entity class with the corresponding mapping.
+
+.. configuration-block::
+
+ .. code-block:: php
+
+
+
+
+
+
+
+
+
+ .. code-block:: yaml
+
+ MyProject\Entity\User:
+ type: entity
+ entityListeners:
+ UserListener:
+ # ....
+
+.. _reference-entity-listeners:
+
+Entity listeners class
+~~~~~~~~~~~~~~~~~~~~~~
+
+An ``Entity Listener`` could be any class, by default it should be a class with a no-arg constructor.
+
+- Different from :ref:`reference-events-implementing-listeners` an ``Entity Listener`` is invoked just to the specified entity
+- An entity listener method receives two arguments, the entity instance and the lifecycle event.
+- A callback method could be defined by naming convention or specifying a method mapping.
+- When the listener mapping is not given the parser will lookup for methods that match with the naming convention.
+- When the listener mapping is given the parser won't lookup for any naming convention.
+
+.. code-block:: php
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: yaml
+
+ MyProject\Entity\User:
+ type: entity
+ entityListeners:
+ UserListener:
+ preFlush: [preFlushHandler]
+ postLoad: [postLoadHandler]
+
+ postPersist: [postPersistHandler]
+ prePersist: [prePersistHandler]
+
+ postUpdate: [postUpdateHandler]
+ preUpdate: [preUpdateHandler]
+
+ postRemove: [postRemoveHandler]
+ preRemove: [preRemoveHandler]
+ # ....
+
+
+
+Entity listeners resolver
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+Doctrine invoke the listener resolver to get the listener instance.
+
+- An resolver allows you register a specific ``Entity Listener`` instance.
+- You can also implement your own resolver by extending ``Doctrine\ORM\Mapping\DefaultEntityListenerResolver`` or implementing ``Doctrine\ORM\Mapping\EntityListenerResolver``
+
+Specifying an entity listener instance :
+
+.. code-block:: php
+
+ service = $service;
+ }
+
+ public function preUpdate(User $user, PreUpdateEventArgs $event)
+ {
+ $this->service->doSomething($user);
+ }
+ }
+
+ // register a entity listener.
+ $listener = $container->get('user_listener');
+ $em->getConfiguration()->getEntityListenerResolver()->register($listener);
+
+Implementing your own resolver :
+
+.. code-block:: php
+
+ container = $container;
+ }
+
+ public function resolve($className)
+ {
+ // resolve the service id by the given class name;
+ $id = 'user_listener';
+
+ return $this->container->get($id);
+ }
+ }
+
+ // configure the listener resolver.
+ $em->getConfiguration()->setEntityListenerResolver($container->get('my_resolver'));
+
Load ClassMetadata Event
------------------------
diff --git a/doctrine-mapping.xsd b/doctrine-mapping.xsd
index e8b2e26b2..243bfcd5b 100644
--- a/doctrine-mapping.xsd
+++ b/doctrine-mapping.xsd
@@ -51,6 +51,7 @@
+
@@ -98,6 +99,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -137,6 +152,7 @@
+
diff --git a/lib/Doctrine/ORM/Configuration.php b/lib/Doctrine/ORM/Configuration.php
index 5e19d29bd..b0a09cbb5 100644
--- a/lib/Doctrine/ORM/Configuration.php
+++ b/lib/Doctrine/ORM/Configuration.php
@@ -29,6 +29,8 @@ use Doctrine\ORM\Mapping\QuoteStrategy;
use Doctrine\ORM\Mapping\DefaultQuoteStrategy;
use Doctrine\ORM\Mapping\NamingStrategy;
use Doctrine\ORM\Mapping\DefaultNamingStrategy;
+use Doctrine\ORM\Mapping\EntityListenerResolver;
+use Doctrine\ORM\Mapping\DefaultEntityListenerResolver;
use Doctrine\Common\Annotations\SimpleAnnotationReader;
use Doctrine\Common\Annotations\CachedReader;
@@ -763,4 +765,30 @@ class Configuration extends \Doctrine\DBAL\Configuration
return $this->_attributes['quoteStrategy'];
}
+
+ /**
+ * Set the entity listener resolver.
+ *
+ * @since 2.4
+ * @param \Doctrine\ORM\Mapping\EntityListenerResolver $resolver
+ */
+ public function setEntityListenerResolver(EntityListenerResolver $resolver)
+ {
+ $this->_attributes['entityListenerResolver'] = $resolver;
+ }
+
+ /**
+ * Get the entity listener resolver.
+ *
+ * @since 2.4
+ * @return \Doctrine\ORM\Mapping\EntityListenerResolver
+ */
+ public function getEntityListenerResolver()
+ {
+ if ( ! isset($this->_attributes['entityListenerResolver'])) {
+ $this->_attributes['entityListenerResolver'] = new DefaultEntityListenerResolver();
+ }
+
+ return $this->_attributes['entityListenerResolver'];
+ }
}
diff --git a/lib/Doctrine/ORM/Event/ListenersInvoker.php b/lib/Doctrine/ORM/Event/ListenersInvoker.php
new file mode 100644
index 000000000..96fa4e67a
--- /dev/null
+++ b/lib/Doctrine/ORM/Event/ListenersInvoker.php
@@ -0,0 +1,120 @@
+.
+ */
+
+namespace Doctrine\ORM\Event;
+
+use Doctrine\ORM\Mapping\ClassMetadata;
+use Doctrine\ORM\EntityManager;
+use Doctrine\Common\EventArgs;
+
+/**
+ * A method invoker based on entity lifecycle.
+ *
+ * @author Fabio B. Silva
+ * @since 2.4
+ */
+class ListenersInvoker
+{
+ const INVOKE_NONE = 0;
+ const INVOKE_LISTENERS = 1;
+ const INVOKE_CALLBACKS = 2;
+ const INVOKE_MANAGER = 4;
+
+ /**
+ * @var \Doctrine\ORM\Mapping\EntityListenerResolver The Entity listener resolver.
+ */
+ private $resolver;
+
+ /**
+ * The EventManager used for dispatching events.
+ *
+ * @var \Doctrine\Common\EventManager
+ */
+ private $eventManager;
+
+ /**
+ * Initializes a new ListenersInvoker instance.
+ *
+ * @param \Doctrine\ORM\EntityManager $em
+ */
+ public function __construct(EntityManager $em)
+ {
+ $this->eventManager = $em->getEventManager();
+ $this->resolver = $em->getConfiguration()->getEntityListenerResolver();
+ }
+
+ /**
+ * Get the subscribed event systems
+ *
+ * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
+ * @param string $eventName The entity lifecycle event.
+ *
+ * @return integer Bitmask of subscribed event systems.
+ */
+ public function getSubscribedSystems(ClassMetadata $metadata, $eventName)
+ {
+ $invoke = self::INVOKE_NONE;
+
+ if (isset($metadata->lifecycleCallbacks[$eventName])) {
+ $invoke |= self::INVOKE_CALLBACKS;
+ }
+
+ if (isset($metadata->entityListeners[$eventName])) {
+ $invoke |= self::INVOKE_LISTENERS;
+ }
+
+ if ($this->eventManager->hasListeners($eventName)) {
+ $invoke |= self::INVOKE_MANAGER;
+ }
+
+ return $invoke;
+ }
+
+ /**
+ * Dispatches the lifecycle event of the given entity.
+ *
+ * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
+ * @param string $eventName The entity lifecycle event.
+ * @param object $entity The Entity on which the event occured.
+ * @param \Doctrine\Common\EventArgs $event The Event args.
+ * @param integer $invoke Bitmask to invoke listeners.
+ */
+ public function invoke(ClassMetadata $metadata, $eventName, $entity, EventArgs $event, $invoke)
+ {
+ if($invoke & self::INVOKE_CALLBACKS) {
+ foreach ($metadata->lifecycleCallbacks[$eventName] as $callback) {
+ $entity->$callback($event);
+ }
+ }
+
+ if($invoke & self::INVOKE_LISTENERS) {
+ foreach ($metadata->entityListeners[$eventName] as $listener) {
+ $class = $listener['class'];
+ $method = $listener['method'];
+ $instance = $this->resolver->resolve($class);
+
+ $instance->$method($entity, $event);
+ }
+ }
+
+ if($invoke & self::INVOKE_MANAGER) {
+ $this->eventManager->dispatchEvent($eventName, $event);
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/Doctrine/ORM/Mapping/Builder/EntityListenerBuilder.php b/lib/Doctrine/ORM/Mapping/Builder/EntityListenerBuilder.php
new file mode 100644
index 000000000..d17abeac5
--- /dev/null
+++ b/lib/Doctrine/ORM/Mapping/Builder/EntityListenerBuilder.php
@@ -0,0 +1,72 @@
+.
+ */
+
+namespace Doctrine\ORM\Mapping\Builder;
+
+use Doctrine\ORM\Mapping\MappingException;
+use Doctrine\ORM\Mapping\ClassMetadata;
+use Doctrine\ORM\Events;
+
+/**
+ * Builder for entity listeners.
+ *
+ * @since 2.4
+ * @author Fabio B. Silva
+ */
+class EntityListenerBuilder
+{
+ /**
+ * @var array Hash-map to handle event names.
+ */
+ static private $events = array(
+ Events::preRemove => true,
+ Events::postRemove => true,
+ Events::prePersist => true,
+ Events::postPersist => true,
+ Events::preUpdate => true,
+ Events::postUpdate => true,
+ Events::postLoad => true,
+ Events::preFlush => true
+ );
+
+ /**
+ * Lookup the entity class to find methods that match to event lifecycle names
+ *
+ * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata.
+ * @param string $className The listener class name.
+ *
+ * @throws \Doctrine\ORM\Mapping\MappingException When the listener class not found.
+ */
+ static public function bindEntityListener(ClassMetadata $metadata, $className)
+ {
+ $class = $metadata->fullyQualifiedClassName($className);
+
+ if ( ! class_exists($class)) {
+ throw MappingException::entityListenerClassNotFound($class, $className);
+ }
+
+ foreach (get_class_methods($class) as $method) {
+ if ( ! isset(self::$events[$method])) {
+ continue;
+ }
+
+ $metadata->addEntityListener($method, $class, $method);
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
index a259efa84..4ea082d31 100644
--- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
+++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
@@ -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) {
@@ -458,7 +462,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
$sequenceName = $this->em->getConfiguration()->getQuoteStrategy()->getSequenceName($definition, $class, $this->targetPlatform);
}
- $generator = ($fieldName && $class->fieldMappings[$fieldName]['type'] === "bigint")
+ $generator = ($fieldName && $class->fieldMappings[$fieldName]['type'] === 'bigint')
? new BigIntegerIdentityGenerator($sequenceName)
: new IdentityGenerator($sequenceName);
diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
index a9dfa9b8c..ec5d44759 100644
--- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
+++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
@@ -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 ClassMetadata instance holds all the object-relational mapping metadata
@@ -436,6 +437,13 @@ class ClassMetadataInfo implements ClassMetadata
*/
public $lifecycleCallbacks = array();
+ /**
+ * READ-ONLY: The registered entity listeners.
+ *
+ * @var array
+ */
+ public $entityListeners = array();
+
/**
* READ-ONLY: The association mappings of this class.
*
@@ -1313,10 +1321,7 @@ class ClassMetadataInfo implements ClassMetadata
$mapping['sourceEntity'] = $this->name;
if (isset($mapping['targetEntity'])) {
- if (strlen($this->namespace) > 0 && strpos($mapping['targetEntity'], '\\') === false) {
- $mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
- }
-
+ $mapping['targetEntity'] = $this->fullyQualifiedClassName($mapping['targetEntity']);
$mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
}
@@ -1904,11 +1909,7 @@ class ClassMetadataInfo implements ClassMetadata
public function setSubclasses(array $subclasses)
{
foreach ($subclasses as $subclass) {
- if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
- $this->subClasses[] = $this->namespace . '\\' . $subclass;
- } else {
- $this->subClasses[] = $subclass;
- }
+ $this->subClasses[] = $this->fullyQualifiedClassName($subclass);
}
}
@@ -2261,11 +2262,9 @@ class ClassMetadataInfo implements ClassMetadata
$queryMapping['isSelfClass'] = true;
$queryMapping['resultClass'] = $this->name;
-
- } else if (strlen($this->namespace) > 0 && strpos($queryMapping['resultClass'], '\\') === false) {
- $queryMapping['resultClass'] = $this->namespace . '\\' . $queryMapping['resultClass'];
}
+ $queryMapping['resultClass'] = $this->fullyQualifiedClassName($queryMapping['resultClass']);
$queryMapping['resultClass'] = ltrim($queryMapping['resultClass'], '\\');
}
@@ -2304,10 +2303,10 @@ class ClassMetadataInfo implements ClassMetadata
$entityResult['isSelfClass'] = true;
$entityResult['entityClass'] = $this->name;
- } else if (strlen($this->namespace) > 0 && strpos($entityResult['entityClass'], '\\') === false) {
- $entityResult['entityClass'] = $this->namespace . '\\' . $entityResult['entityClass'];
}
+ $entityResult['entityClass'] = $this->fullyQualifiedClassName($entityResult['entityClass']);
+
$resultMapping['entities'][$key]['entityClass'] = ltrim($entityResult['entityClass'], '\\');
$resultMapping['entities'][$key]['isSelfClass'] = $entityResult['isSelfClass'];
@@ -2419,17 +2418,15 @@ class ClassMetadataInfo implements ClassMetadata
*/
public function setCustomRepositoryClass($repositoryClassName)
{
- if ($repositoryClassName !== null && strpos($repositoryClassName, '\\') === false
- && strlen($this->namespace) > 0) {
- $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
- }
- $this->customRepositoryClassName = $repositoryClassName;
+ $this->customRepositoryClassName = $this->fullyQualifiedClassName($repositoryClassName);
}
/**
* Dispatches the lifecycle event of the given entity to the registered
* lifecycle callbacks and lifecycle listeners.
*
+ * @deprecated Deprecated since version 2.4 in favor of \Doctrine\ORM\Event\ListenersInvoker
+ *
* @param string $lifecycleEvent The lifecycle event.
* @param object $entity The Entity on which the event occured.
*
@@ -2492,6 +2489,33 @@ class ClassMetadataInfo implements ClassMetadata
$this->lifecycleCallbacks = $callbacks;
}
+ /**
+ * Adds a entity listener for entities of this class.
+ *
+ * @param string $eventName The entity lifecycle event.
+ * @param string $class The listener class.
+ * @param string $method The listener callback method.
+ *
+ * @throws \Doctrine\ORM\Mapping\MappingException
+ */
+ public function addEntityListener($eventName, $class, $method)
+ {
+ $class = $this->fullyQualifiedClassName($class);
+
+ if ( ! class_exists($class)) {
+ throw MappingException::entityListenerClassNotFound($class, $this->name);
+ }
+
+ if ( ! method_exists($class, $method)) {
+ throw MappingException::entityListenerMethodNotFound($class, $method, $this->name);
+ }
+
+ $this->entityListeners[$eventName][] = array(
+ 'class' => $class,
+ 'method' => $method
+ );
+ }
+
/**
* Sets the discriminator column definition.
*
@@ -2557,10 +2581,7 @@ class ClassMetadataInfo implements ClassMetadata
*/
public function addDiscriminatorMapClass($name, $className)
{
- if (strlen($this->namespace) > 0 && strpos($className, '\\') === false) {
- $className = $this->namespace . '\\' . $className;
- }
-
+ $className = $this->fullyQualifiedClassName($className);
$className = ltrim($className, '\\');
$this->discriminatorMap[$name] = $className;
@@ -2982,4 +3003,17 @@ class ClassMetadataInfo implements ClassMetadata
}
return $relations;
}
+
+ /**
+ * @param string $className
+ * @return string
+ */
+ public function fullyQualifiedClassName($className)
+ {
+ if ($className !== null && strpos($className, '\\') === false && strlen($this->namespace) > 0) {
+ return $this->namespace . '\\' . $className;
+ }
+
+ return $className;
+ }
}
diff --git a/lib/Doctrine/ORM/Mapping/DefaultEntityListenerResolver.php b/lib/Doctrine/ORM/Mapping/DefaultEntityListenerResolver.php
new file mode 100644
index 000000000..78c93791b
--- /dev/null
+++ b/lib/Doctrine/ORM/Mapping/DefaultEntityListenerResolver.php
@@ -0,0 +1,75 @@
+.
+ */
+
+namespace Doctrine\ORM\Mapping;
+
+/**
+ * The default DefaultEntityListene
+ *
+ * @since 2.4
+ * @author Fabio B. Silva
+ */
+class DefaultEntityListenerResolver implements EntityListenerResolver
+{
+ /**
+ * @var array Map to store entity listener instances.
+ */
+ private $instances = array();
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear($className = null)
+ {
+ if ($className === null) {
+ $this->instances = array();
+
+ return;
+ }
+
+ if (isset($this->instances[$className = trim($className, '\\')])) {
+ unset($this->instances[$className]);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function register($object)
+ {
+ if ( ! is_object($object)) {
+ throw new \InvalidArgumentException(sprintf('An object was expected, but got "%s".', gettype($object)));
+ }
+
+ $this->instances[get_class($object)] = $object;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function resolve($className)
+ {
+ if (isset($this->instances[$className = trim($className, '\\')])) {
+ return $this->instances[$className];
+ }
+
+ return $this->instances[$className] = new $className();
+ }
+}
\ No newline at end of file
diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php
index 004b55c5b..a4bea98ea 100644
--- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php
+++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php
@@ -23,8 +23,10 @@ use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\Column;
+use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
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.
@@ -384,9 +386,8 @@ class AnnotationDriver extends AbstractAnnotationDriver
// Check for JoinTable annotations
if ($associationOverride->joinTable) {
- $joinTable = null;
$joinTableAnnot = $associationOverride->joinTable;
- $joinTable = array(
+ $joinTable = array(
'name' => $joinTableAnnot->name,
'schema' => $joinTableAnnot->schema
);
@@ -415,54 +416,47 @@ class AnnotationDriver extends AbstractAnnotationDriver
}
}
+ // Evaluate EntityListeners annotation
+ if (isset($classAnnotations['Doctrine\ORM\Mapping\EntityListeners'])) {
+ $entityListenersAnnot = $classAnnotations['Doctrine\ORM\Mapping\EntityListeners'];
+
+ foreach ($entityListenersAnnot->value as $item) {
+ $listenerClassName = $metadata->fullyQualifiedClassName($item);
+
+ if ( ! class_exists($listenerClassName)) {
+ throw MappingException::entityListenerClassNotFound($listenerClassName, $className);
+ }
+
+ $hasMapping = false;
+ $listenerClass = new \ReflectionClass($listenerClassName);
+ /* @var $method \ReflectionMethod */
+ foreach ($listenerClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
+ // find method callbacks.
+ $callbacks = $this->getMethodCallbacks($method);
+ $hasMapping = $hasMapping ?: ( ! empty($callbacks));
+
+ foreach ($callbacks as $value) {
+ $metadata->addEntityListener($value[1], $listenerClassName, $value[0]);
+ }
+ }
+ // Evaluate the listener using naming convention.
+ if ( ! $hasMapping ) {
+ EntityListenerBuilder::bindEntityListener($metadata, $listenerClassName);
+ }
+ }
+ }
+
// 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) {
+ $metadata->addLifecycleCallback($value[0], $value[1]);
}
}
}
@@ -488,9 +482,56 @@ 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
*/
diff --git a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php
index 04bf2dedc..14abadb9e 100644
--- a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php
+++ b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php
@@ -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';
\ No newline at end of file
diff --git a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php
index b8b5e13b2..26d1becd0 100644
--- a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php
+++ b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php
@@ -19,10 +19,11 @@
namespace Doctrine\ORM\Mapping\Driver;
-use SimpleXMLElement,
- Doctrine\Common\Persistence\Mapping\Driver\FileDriver,
- Doctrine\Common\Persistence\Mapping\ClassMetadata,
- Doctrine\ORM\Mapping\MappingException;
+use SimpleXMLElement;
+use Doctrine\Common\Persistence\Mapping\Driver\FileDriver;
+use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
+use Doctrine\Common\Persistence\Mapping\ClassMetadata;
+use Doctrine\ORM\Mapping\MappingException;
/**
* XmlDriver is a metadata driver that enables mapping through XML files.
@@ -556,6 +557,26 @@ class XmlDriver extends FileDriver
$metadata->addLifecycleCallback((string)$lifecycleCallback['method'], constant('Doctrine\ORM\Events::' . (string)$lifecycleCallback['type']));
}
}
+
+ // Evaluate entity listener
+ if (isset($xmlRoot->{'entity-listeners'})) {
+ foreach ($xmlRoot->{'entity-listeners'}->{'entity-listener'} as $listenerElement) {
+ $className = (string) $listenerElement['class'];
+ // Evaluate the listener using naming convention.
+ if($listenerElement->count() === 0) {
+ EntityListenerBuilder::bindEntityListener($metadata, $className);
+
+ continue;
+ }
+
+ foreach ($listenerElement as $callbackElement) {
+ $eventName = (string) $callbackElement['type'];
+ $methodName = (string) $callbackElement['method'];
+
+ $metadata->addEntityListener($eventName, $className, $methodName);
+ }
+ }
+ }
}
/**
diff --git a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php
index 50e51ed05..bf97f19fb 100644
--- a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php
+++ b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php
@@ -20,6 +20,7 @@
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
+use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
use Doctrine\Common\Persistence\Mapping\Driver\FileDriver;
use Doctrine\ORM\Mapping\MappingException;
use Symfony\Component\Yaml\Yaml;
@@ -572,6 +573,24 @@ class YamlDriver extends FileDriver
}
}
}
+
+ // Evaluate entityListeners
+ if (isset($element['entityListeners'])) {
+ foreach ($element['entityListeners'] as $className => $entityListener) {
+ // Evaluate the listener using naming convention.
+ if (empty($entityListener)) {
+ EntityListenerBuilder::bindEntityListener($metadata, $className);
+
+ continue;
+ }
+
+ foreach ($entityListener as $eventName => $callbackElement){
+ foreach ($callbackElement as $methodName){
+ $metadata->addEntityListener($eventName, $className, $methodName);
+ }
+ }
+ }
+ }
}
/**
diff --git a/lib/Doctrine/ORM/Mapping/EntityListenerResolver.php b/lib/Doctrine/ORM/Mapping/EntityListenerResolver.php
new file mode 100644
index 000000000..bbf498dc3
--- /dev/null
+++ b/lib/Doctrine/ORM/Mapping/EntityListenerResolver.php
@@ -0,0 +1,55 @@
+.
+ */
+
+namespace Doctrine\ORM\Mapping;
+
+/**
+ * A resolver is used to instantiate an entity listener.
+ *
+ * @since 2.4
+ * @author Fabio B. Silva
+ */
+interface EntityListenerResolver
+{
+ /**
+ * Clear all instances from the set, or a specific class when given.
+ *
+ * @param string $className The fully-qualified class name
+ *
+ * @return void
+ */
+ function clear($className = null);
+
+ /**
+ * Returns a entity listener instance for the given class name.
+ *
+ * @param string $className The fully-qualified class name
+ *
+ * @return object An entity listener
+ */
+ function resolve($className);
+
+ /**
+ * Register a entity listener instance.
+ *
+ * @return object An entity listener
+ */
+ function register($object);
+}
\ No newline at end of file
diff --git a/lib/Doctrine/ORM/Mapping/EntityListeners.php b/lib/Doctrine/ORM/Mapping/EntityListeners.php
new file mode 100644
index 000000000..d9478a497
--- /dev/null
+++ b/lib/Doctrine/ORM/Mapping/EntityListeners.php
@@ -0,0 +1,41 @@
+.
+ */
+
+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
+ * @since 2.4
+ *
+ * @Annotation
+ * @Target("CLASS")
+ */
+final class EntityListeners implements Annotation
+{
+ /**
+ * Specifies the names of the entity listeners.
+ *
+ * @var array
+ */
+ public $value = array();
+}
\ No newline at end of file
diff --git a/lib/Doctrine/ORM/Mapping/MappingException.php b/lib/Doctrine/ORM/Mapping/MappingException.php
index 3c32eebd5..61ecb4d11 100644
--- a/lib/Doctrine/ORM/Mapping/MappingException.php
+++ b/lib/Doctrine/ORM/Mapping/MappingException.php
@@ -684,6 +684,29 @@ class MappingException extends \Doctrine\ORM\ORMException
return new self("Entity '" . $className . "' has no method '" . $methodName . "' to be registered as lifecycle callback.");
}
+ /**
+ * @param string $className
+ * @param string $methodName
+ *
+ * @return \Doctrine\ORM\Mapping\MappingException
+ */
+ public static function entityListenerClassNotFound($listenerName, $className)
+ {
+ return new self(sprintf('Entity Listener "%s" declared on "%s" not found.', $listenerName, $className));
+ }
+
+ /**
+ * @param string $listenerName
+ * @param string $methodName
+ * @param string $className
+ *
+ * @return \Doctrine\ORM\Mapping\MappingException
+ */
+ public static function entityListenerMethodNotFound($listenerName, $methodName, $className)
+ {
+ return new self(sprintf('Entity Listener "%s" declared on "%s" has no method "%s".', $listenerName, $className, $methodName));
+ }
+
/**
* @param string $className
* @param string $annotation
diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php
index 9f54463c1..1d2919611 100644
--- a/lib/Doctrine/ORM/UnitOfWork.php
+++ b/lib/Doctrine/ORM/UnitOfWork.php
@@ -22,15 +22,22 @@ namespace Doctrine\ORM;
use Exception;
use InvalidArgumentException;
use UnexpectedValueException;
+
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\NotifyPropertyChanged;
use Doctrine\Common\PropertyChangedListener;
use Doctrine\Common\Persistence\ObjectManagerAware;
-use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Proxy\Proxy;
+use Doctrine\ORM\Event\LifecycleEventArgs;
+use Doctrine\ORM\Event\PreUpdateEventArgs;
+use Doctrine\ORM\Event\PreFlushEventArgs;
+use Doctrine\ORM\Event\OnFlushEventArgs;
+use Doctrine\ORM\Event\PostFlushEventArgs;
+use Doctrine\ORM\Event\ListenersInvoker;
+
/**
* The UnitOfWork is responsible for tracking changes to objects during an
* "object-level" transaction and for writing out changes to the database
@@ -212,6 +219,13 @@ class UnitOfWork implements PropertyChangedListener
*/
private $evm;
+ /**
+ * The ListenersInvoker used for dispatching events.
+ *
+ * @var \Doctrine\ORM\Event\ListenersInvoker
+ */
+ private $listenersInvoker;
+
/**
* Orphaned entities that are scheduled for removal.
*
@@ -240,8 +254,9 @@ class UnitOfWork implements PropertyChangedListener
*/
public function __construct(EntityManager $em)
{
- $this->em = $em;
- $this->evm = $em->getEventManager();
+ $this->em = $em;
+ $this->evm = $em->getEventManager();
+ $this->listenersInvoker = new ListenersInvoker($em);
}
/**
@@ -267,7 +282,7 @@ class UnitOfWork implements PropertyChangedListener
{
// Raise preFlush
if ($this->evm->hasListeners(Events::preFlush)) {
- $this->evm->dispatchEvent(Events::preFlush, new Event\PreFlushEventArgs($this->em));
+ $this->evm->dispatchEvent(Events::preFlush, new PreFlushEventArgs($this->em));
}
// Compute changes done since last commit.
@@ -506,9 +521,10 @@ class UnitOfWork implements PropertyChangedListener
$class = $this->em->getClassMetadata(get_class($entity));
}
- // Fire PreFlush lifecycle callbacks
- if (isset($class->lifecycleCallbacks[Events::preFlush])) {
- $class->invokeLifecycleCallbacks(Events::preFlush, $entity);
+ $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preFlush);
+
+ if ($invoke !== ListenersInvoker::INVOKE_NONE) {
+ $this->listenersInvoker->invoke($class, Events::preFlush, $entity, new PreFlushEventArgs($entity, $this->em), $invoke);
}
$actualData = array();
@@ -798,21 +814,18 @@ class UnitOfWork implements PropertyChangedListener
}
/**
- * @param ClassMetadata $class
+ * @param \Doctrine\ORM\Mapping\ClassMetadata $class
* @param object $entity
*
* @return void
*/
private function persistNew($class, $entity)
{
- $oid = spl_object_hash($entity);
+ $oid = spl_object_hash($entity);
+ $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::prePersist);
- if (isset($class->lifecycleCallbacks[Events::prePersist])) {
- $class->invokeLifecycleCallbacks(Events::prePersist, $entity);
- }
-
- if ($this->evm->hasListeners(Events::prePersist)) {
- $this->evm->dispatchEvent(Events::prePersist, new LifecycleEventArgs($entity, $this->em));
+ if ($invoke !== ListenersInvoker::INVOKE_NONE) {
+ $this->listenersInvoker->invoke($class, Events::prePersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
}
$idGen = $class->idGenerator;
@@ -908,12 +921,10 @@ class UnitOfWork implements PropertyChangedListener
*/
private function executeInserts($class)
{
- $className = $class->name;
- $persister = $this->getEntityPersister($className);
- $entities = array();
-
- $hasLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postPersist]);
- $hasListeners = $this->evm->hasListeners(Events::postPersist);
+ $entities = array();
+ $className = $class->name;
+ $persister = $this->getEntityPersister($className);
+ $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postPersist);
foreach ($this->entityInsertions as $oid => $entity) {
if ($this->em->getClassMetadata(get_class($entity))->name !== $className) {
@@ -924,7 +935,7 @@ class UnitOfWork implements PropertyChangedListener
unset($this->entityInsertions[$oid]);
- if ($hasLifecycleCallbacks || $hasListeners) {
+ if ($invoke !== ListenersInvoker::INVOKE_NONE) {
$entities[] = $entity;
}
}
@@ -948,13 +959,7 @@ class UnitOfWork implements PropertyChangedListener
}
foreach ($entities as $entity) {
- if ($hasLifecycleCallbacks) {
- $class->invokeLifecycleCallbacks(Events::postPersist, $entity);
- }
-
- if ($hasListeners) {
- $this->evm->dispatchEvent(Events::postPersist, new LifecycleEventArgs($entity, $this->em));
- }
+ $this->listenersInvoker->invoke($class, Events::postPersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
}
}
@@ -967,45 +972,29 @@ class UnitOfWork implements PropertyChangedListener
*/
private function executeUpdates($class)
{
- $className = $class->name;
- $persister = $this->getEntityPersister($className);
-
- $hasPreUpdateLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::preUpdate]);
- $hasPreUpdateListeners = $this->evm->hasListeners(Events::preUpdate);
-
- $hasPostUpdateLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postUpdate]);
- $hasPostUpdateListeners = $this->evm->hasListeners(Events::postUpdate);
+ $className = $class->name;
+ $persister = $this->getEntityPersister($className);
+ $preUpdateInvoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preUpdate);
+ $postUpdateInvoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postUpdate);
foreach ($this->entityUpdates as $oid => $entity) {
if ($this->em->getClassMetadata(get_class($entity))->name !== $className) {
continue;
}
- if ($hasPreUpdateLifecycleCallbacks) {
- $class->invokeLifecycleCallbacks(Events::preUpdate, $entity);
-
+ if ($preUpdateInvoke != ListenersInvoker::INVOKE_NONE) {
+ $this->listenersInvoker->invoke($class, Events::preUpdate, $entity, new PreUpdateEventArgs($entity, $this->em, $this->entityChangeSets[$oid]), $preUpdateInvoke);
$this->recomputeSingleEntityChangeSet($class, $entity);
}
- if ($hasPreUpdateListeners) {
- $this->evm->dispatchEvent(
- Events::preUpdate,
- new Event\PreUpdateEventArgs($entity, $this->em, $this->entityChangeSets[$oid])
- );
- }
-
- if (!empty($this->entityChangeSets[$oid])) {
+ if ( ! empty($this->entityChangeSets[$oid])) {
$persister->update($entity);
}
unset($this->entityUpdates[$oid]);
- if ($hasPostUpdateLifecycleCallbacks) {
- $class->invokeLifecycleCallbacks(Events::postUpdate, $entity);
- }
-
- if ($hasPostUpdateListeners) {
- $this->evm->dispatchEvent(Events::postUpdate, new LifecycleEventArgs($entity, $this->em));
+ if ($postUpdateInvoke != ListenersInvoker::INVOKE_NONE) {
+ $this->listenersInvoker->invoke($class, Events::postUpdate, $entity, new LifecycleEventArgs($entity, $this->em), $postUpdateInvoke);
}
}
}
@@ -1019,11 +1008,9 @@ class UnitOfWork implements PropertyChangedListener
*/
private function executeDeletions($class)
{
- $className = $class->name;
- $persister = $this->getEntityPersister($className);
-
- $hasLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postRemove]);
- $hasListeners = $this->evm->hasListeners(Events::postRemove);
+ $className = $class->name;
+ $persister = $this->getEntityPersister($className);
+ $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postRemove);
foreach ($this->entityDeletions as $oid => $entity) {
if ($this->em->getClassMetadata(get_class($entity))->name !== $className) {
@@ -1046,12 +1033,8 @@ class UnitOfWork implements PropertyChangedListener
$class->reflFields[$class->identifier[0]]->setValue($entity, null);
}
- if ($hasLifecycleCallbacks) {
- $class->invokeLifecycleCallbacks(Events::postRemove, $entity);
- }
-
- if ($hasListeners) {
- $this->evm->dispatchEvent(Events::postRemove, new LifecycleEventArgs($entity, $this->em));
+ if ($invoke !== ListenersInvoker::INVOKE_NONE) {
+ $this->listenersInvoker->invoke($class, Events::postRemove, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
}
}
}
@@ -1691,12 +1674,10 @@ class UnitOfWork implements PropertyChangedListener
break;
case self::STATE_MANAGED:
- if (isset($class->lifecycleCallbacks[Events::preRemove])) {
- $class->invokeLifecycleCallbacks(Events::preRemove, $entity);
- }
+ $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preRemove);
- if ($this->evm->hasListeners(Events::preRemove)) {
- $this->evm->dispatchEvent(Events::preRemove, new LifecycleEventArgs($entity, $this->em));
+ if ($invoke !== ListenersInvoker::INVOKE_NONE) {
+ $this->listenersInvoker->invoke($class, Events::preRemove, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
}
$this->scheduleForDelete($entity);
@@ -2695,13 +2676,10 @@ class UnitOfWork implements PropertyChangedListener
}
if ($overrideLocalValues) {
- if (isset($class->lifecycleCallbacks[Events::postLoad])) {
- $class->invokeLifecycleCallbacks(Events::postLoad, $entity);
- }
+ $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postLoad);
-
- if ($this->evm->hasListeners(Events::postLoad)) {
- $this->evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->em));
+ if ($invoke !== ListenersInvoker::INVOKE_NONE) {
+ $this->listenersInvoker->invoke($class, Events::postLoad, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
}
}
@@ -3177,14 +3155,14 @@ class UnitOfWork implements PropertyChangedListener
private function dispatchOnFlushEvent()
{
if ($this->evm->hasListeners(Events::onFlush)) {
- $this->evm->dispatchEvent(Events::onFlush, new Event\OnFlushEventArgs($this->em));
+ $this->evm->dispatchEvent(Events::onFlush, new OnFlushEventArgs($this->em));
}
}
private function dispatchPostFlushEvent()
{
if ($this->evm->hasListeners(Events::postFlush)) {
- $this->evm->dispatchEvent(Events::postFlush, new Event\PostFlushEventArgs($this->em));
+ $this->evm->dispatchEvent(Events::postFlush, new PostFlushEventArgs($this->em));
}
}
}
diff --git a/tests/Doctrine/Tests/Models/CMS/CmsAddress.php b/tests/Doctrine/Tests/Models/CMS/CmsAddress.php
index 9833f3dfb..aaf4956d3 100644
--- a/tests/Doctrine/Tests/Models/CMS/CmsAddress.php
+++ b/tests/Doctrine/Tests/Models/CMS/CmsAddress.php
@@ -59,6 +59,7 @@ namespace Doctrine\Tests\Models\CMS;
* )
* })
*
+ * @EntityListeners({"CmsAddressListener"})
*/
class CmsAddress
{
@@ -127,6 +128,28 @@ class CmsAddress
'name' => 'company_person',
));
+ $metadata->mapField(array (
+ 'id' => true,
+ 'fieldName' => 'id',
+ 'type' => 'integer',
+ ));
+
+ $metadata->mapField(array (
+ 'fieldName' => 'zip',
+ 'length' => 50,
+ ));
+
+ $metadata->mapField(array (
+ 'fieldName' => 'city',
+ 'length' => 50,
+ ));
+
+ $metadata->mapOneToOne(array(
+ 'fieldName' => 'user',
+ 'targetEntity' => 'CmsUser',
+ 'joinColumns' => array(array('referencedColumnName' => 'id'))
+ ));
+
$metadata->addNamedNativeQuery(array (
'name' => 'find-all',
'query' => 'SELECT id, country, city FROM cms_addresses',
@@ -145,7 +168,6 @@ class CmsAddress
'resultSetMapping' => 'mapping-count',
));
-
$metadata->addSqlResultSetMapping(array (
'name' => 'mapping-find-all',
'columns' => array(),
@@ -187,5 +209,17 @@ class CmsAddress
),
)
));
+
+ $metadata->addEntityListener(\Doctrine\ORM\Events::postPersist, 'CmsAddressListener', 'postPersist');
+ $metadata->addEntityListener(\Doctrine\ORM\Events::prePersist, 'CmsAddressListener', 'prePersist');
+
+ $metadata->addEntityListener(\Doctrine\ORM\Events::postUpdate, 'CmsAddressListener', 'postUpdate');
+ $metadata->addEntityListener(\Doctrine\ORM\Events::preUpdate, 'CmsAddressListener', 'preUpdate');
+
+ $metadata->addEntityListener(\Doctrine\ORM\Events::postRemove, 'CmsAddressListener', 'postRemove');
+ $metadata->addEntityListener(\Doctrine\ORM\Events::preRemove, 'CmsAddressListener', 'preRemove');
+
+ $metadata->addEntityListener(\Doctrine\ORM\Events::preFlush, 'CmsAddressListener', 'preFlush');
+ $metadata->addEntityListener(\Doctrine\ORM\Events::postLoad, 'CmsAddressListener', 'postLoad');
}
}
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/Models/CMS/CmsAddressListener.php b/tests/Doctrine/Tests/Models/CMS/CmsAddressListener.php
new file mode 100644
index 000000000..9c9db89eb
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/CMS/CmsAddressListener.php
@@ -0,0 +1,58 @@
+calls[__FUNCTION__][] = func_get_args();
+ }
+
+ public function postPersist()
+ {
+ $this->calls[__FUNCTION__][] = func_get_args();
+ }
+
+ public function preUpdate()
+ {
+ $this->calls[__FUNCTION__][] = func_get_args();
+ }
+
+ public function postUpdate()
+ {
+ $this->calls[__FUNCTION__][] = func_get_args();
+ }
+
+ public function preRemove()
+ {
+ $this->calls[__FUNCTION__][] = func_get_args();
+ }
+
+ public function postRemove()
+ {
+ $this->calls[__FUNCTION__][] = func_get_args();
+ }
+
+ public function postLoad()
+ {
+ $this->calls[__FUNCTION__][] = func_get_args();
+ }
+
+ public function preFlush()
+ {
+ $this->calls[__FUNCTION__][] = func_get_args();
+ }
+
+ protected function postPersistHandler()
+ {
+ throw new \BadMethodCallException("This is not a valid callback");
+ }
+
+ protected function prePersistHandler()
+ {
+ throw new \BadMethodCallException("This is not a valid callback");
+ }
+}
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/Models/Company/CompanyContract.php b/tests/Doctrine/Tests/Models/Company/CompanyContract.php
index bc8503dfe..139098274 100644
--- a/tests/Doctrine/Tests/Models/Company/CompanyContract.php
+++ b/tests/Doctrine/Tests/Models/Company/CompanyContract.php
@@ -7,6 +7,7 @@ namespace Doctrine\Tests\Models\Company;
* @Table(name="company_contracts")
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(name="discr", type="string")
+ * @EntityListeners({"CompanyContractListener"})
* @DiscriminatorMap({
* "fix" = "CompanyFixContract",
* "flexible" = "CompanyFlexContract",
@@ -128,4 +129,44 @@ abstract class CompanyContract
}
abstract public function calculatePrice();
+
+ static public function loadMetadata(\Doctrine\ORM\Mapping\ClassMetadataInfo $metadata)
+ {
+ $metadata->setInheritanceType(\Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_JOINED);
+ $metadata->setTableName( 'company_contracts');
+ $metadata->setDiscriminatorColumn(array(
+ 'name' => 'discr',
+ 'type' => 'string',
+ ));
+
+ $metadata->mapField(array(
+ 'id' => true,
+ 'name' => 'id',
+ 'fieldName' => 'id',
+ ));
+
+ $metadata->mapField(array(
+ 'type' => 'boolean',
+ 'name' => 'completed',
+ 'fieldName' => 'completed',
+ ));
+
+ $metadata->setDiscriminatorMap(array(
+ "fix" => "CompanyFixContract",
+ "flexible" => "CompanyFlexContract",
+ "flexultra" => "CompanyFlexUltraContract"
+ ));
+
+ $metadata->addEntityListener(\Doctrine\ORM\Events::postPersist, 'CompanyContractListener', 'postPersistHandler');
+ $metadata->addEntityListener(\Doctrine\ORM\Events::prePersist, 'CompanyContractListener', 'prePersistHandler');
+
+ $metadata->addEntityListener(\Doctrine\ORM\Events::postUpdate, 'CompanyContractListener', 'postUpdateHandler');
+ $metadata->addEntityListener(\Doctrine\ORM\Events::preUpdate, 'CompanyContractListener', 'preUpdateHandler');
+
+ $metadata->addEntityListener(\Doctrine\ORM\Events::postRemove, 'CompanyContractListener', 'postRemoveHandler');
+ $metadata->addEntityListener(\Doctrine\ORM\Events::preRemove, 'CompanyContractListener', 'preRemoveHandler');
+
+ $metadata->addEntityListener(\Doctrine\ORM\Events::preFlush, 'CompanyContractListener', 'preFlushHandler');
+ $metadata->addEntityListener(\Doctrine\ORM\Events::postLoad, 'CompanyContractListener', 'postLoadHandler');
+ }
}
diff --git a/tests/Doctrine/Tests/Models/Company/CompanyContractListener.php b/tests/Doctrine/Tests/Models/Company/CompanyContractListener.php
new file mode 100644
index 000000000..61c1d4719
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/Company/CompanyContractListener.php
@@ -0,0 +1,84 @@
+postPersistCalls[] = func_get_args();
+ }
+
+ /**
+ * @PrePersist
+ */
+ public function prePersistHandler(CompanyContract $contract)
+ {
+ $this->prePersistCalls[] = func_get_args();
+ }
+
+ /**
+ * @PostUpdate
+ */
+ public function postUpdateHandler(CompanyContract $contract)
+ {
+ $this->postUpdateCalls[] = func_get_args();
+ }
+
+ /**
+ * @PreUpdate
+ */
+ public function preUpdateHandler(CompanyContract $contract)
+ {
+ $this->preUpdateCalls[] = func_get_args();
+ }
+
+ /**
+ * @PostRemove
+ */
+ public function postRemoveHandler(CompanyContract $contract)
+ {
+ $this->postRemoveCalls[] = func_get_args();
+ }
+
+ /**
+ * @PreRemove
+ */
+ public function preRemoveHandler(CompanyContract $contract)
+ {
+ $this->preRemoveCalls[] = func_get_args();
+ }
+
+ /**
+ * @PreFlush
+ */
+ public function preFlushHandler(CompanyContract $contract)
+ {
+ $this->preFlushCalls[] = func_get_args();
+ }
+
+ /**
+ * @PostLoad
+ */
+ public function postLoadHandler(CompanyContract $contract)
+ {
+ $this->postLoadCalls[] = func_get_args();
+ }
+
+}
diff --git a/tests/Doctrine/Tests/Models/Company/CompanyFixContract.php b/tests/Doctrine/Tests/Models/Company/CompanyFixContract.php
index 9186fc3b1..87f3ce952 100644
--- a/tests/Doctrine/Tests/Models/Company/CompanyFixContract.php
+++ b/tests/Doctrine/Tests/Models/Company/CompanyFixContract.php
@@ -27,4 +27,13 @@ class CompanyFixContract extends CompanyContract
{
$this->fixPrice = $fixPrice;
}
+
+ static public function loadMetadata(\Doctrine\ORM\Mapping\ClassMetadataInfo $metadata)
+ {
+ $metadata->mapField(array(
+ 'type' => 'integer',
+ 'name' => 'fixPrice',
+ 'fieldName' => 'fixPrice',
+ ));
+ }
}
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/Models/Company/CompanyFlexContract.php b/tests/Doctrine/Tests/Models/Company/CompanyFlexContract.php
index 121d8ec8e..86e6b330c 100644
--- a/tests/Doctrine/Tests/Models/Company/CompanyFlexContract.php
+++ b/tests/Doctrine/Tests/Models/Company/CompanyFlexContract.php
@@ -107,4 +107,19 @@ class CompanyFlexContract extends CompanyContract
{
$this->managers->removeElement($manager);
}
+
+ static public function loadMetadata(\Doctrine\ORM\Mapping\ClassMetadataInfo $metadata)
+ {
+ $metadata->mapField(array(
+ 'type' => 'integer',
+ 'name' => 'hoursWorked',
+ 'fieldName' => 'hoursWorked',
+ ));
+
+ $metadata->mapField(array(
+ 'type' => 'integer',
+ 'name' => 'pricePerHour',
+ 'fieldName' => 'pricePerHour',
+ ));
+ }
}
diff --git a/tests/Doctrine/Tests/Models/Company/CompanyFlexUltraContract.php b/tests/Doctrine/Tests/Models/Company/CompanyFlexUltraContract.php
index b9ad3d4c9..85126bd0e 100644
--- a/tests/Doctrine/Tests/Models/Company/CompanyFlexUltraContract.php
+++ b/tests/Doctrine/Tests/Models/Company/CompanyFlexUltraContract.php
@@ -4,6 +4,7 @@ namespace Doctrine\Tests\Models\Company;
/**
* @Entity
+ * @EntityListeners({"CompanyContractListener","CompanyFlexUltraContractListener"})
*/
class CompanyFlexUltraContract extends CompanyFlexContract
{
@@ -27,4 +28,27 @@ class CompanyFlexUltraContract extends CompanyFlexContract
{
$this->maxPrice = $maxPrice;
}
+
+ static public function loadMetadata(\Doctrine\ORM\Mapping\ClassMetadataInfo $metadata)
+ {
+ $metadata->mapField(array(
+ 'type' => 'integer',
+ 'name' => 'maxPrice',
+ 'fieldName' => 'maxPrice',
+ ));
+ $metadata->addEntityListener(\Doctrine\ORM\Events::postPersist, 'CompanyContractListener', 'postPersistHandler');
+ $metadata->addEntityListener(\Doctrine\ORM\Events::prePersist, 'CompanyContractListener', 'prePersistHandler');
+
+ $metadata->addEntityListener(\Doctrine\ORM\Events::postUpdate, 'CompanyContractListener', 'postUpdateHandler');
+ $metadata->addEntityListener(\Doctrine\ORM\Events::preUpdate, 'CompanyContractListener', 'preUpdateHandler');
+
+ $metadata->addEntityListener(\Doctrine\ORM\Events::postRemove, 'CompanyContractListener', 'postRemoveHandler');
+ $metadata->addEntityListener(\Doctrine\ORM\Events::preRemove, 'CompanyContractListener', 'preRemoveHandler');
+
+ $metadata->addEntityListener(\Doctrine\ORM\Events::preFlush, 'CompanyContractListener', 'preFlushHandler');
+ $metadata->addEntityListener(\Doctrine\ORM\Events::postLoad, 'CompanyContractListener', 'postLoadHandler');
+
+ $metadata->addEntityListener(\Doctrine\ORM\Events::prePersist, 'CompanyFlexUltraContractListener', 'prePersistHandler1');
+ $metadata->addEntityListener(\Doctrine\ORM\Events::prePersist, 'CompanyFlexUltraContractListener', 'prePersistHandler2');
+ }
}
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/Models/Company/CompanyFlexUltraContractListener.php b/tests/Doctrine/Tests/Models/Company/CompanyFlexUltraContractListener.php
new file mode 100644
index 000000000..2f028e35f
--- /dev/null
+++ b/tests/Doctrine/Tests/Models/Company/CompanyFlexUltraContractListener.php
@@ -0,0 +1,26 @@
+prePersistCalls[] = func_get_args();
+ }
+
+ /**
+ * @PrePersist
+ */
+ public function prePersistHandler2(CompanyContract $contract, LifecycleEventArgs $args)
+ {
+ $this->prePersistCalls[] = func_get_args();
+ }
+}
diff --git a/tests/Doctrine/Tests/ORM/ConfigurationTest.php b/tests/Doctrine/Tests/ORM/ConfigurationTest.php
index 1caaf5701..5f805fb86 100644
--- a/tests/Doctrine/Tests/ORM/ConfigurationTest.php
+++ b/tests/Doctrine/Tests/ORM/ConfigurationTest.php
@@ -260,6 +260,18 @@ class ConfigurationTest extends PHPUnit_Framework_TestCase
$this->configuration->setQuoteStrategy($quoteStrategy);
$this->assertSame($quoteStrategy, $this->configuration->getQuoteStrategy());
}
+
+ /**
+ * @group DDC-1955
+ */
+ public function testSetGetEntityListenerResolver()
+ {
+ $this->assertInstanceOf('Doctrine\ORM\Mapping\EntityListenerResolver', $this->configuration->getEntityListenerResolver());
+ $this->assertInstanceOf('Doctrine\ORM\Mapping\DefaultEntityListenerResolver', $this->configuration->getEntityListenerResolver());
+ $resolver = $this->getMock('Doctrine\ORM\Mapping\EntityListenerResolver');
+ $this->configuration->setEntityListenerResolver($resolver);
+ $this->assertSame($resolver, $this->configuration->getEntityListenerResolver());
+ }
}
class ConfigurationTestAnnotationReaderChecker
diff --git a/tests/Doctrine/Tests/ORM/Functional/EntityListenersTest.php b/tests/Doctrine/Tests/ORM/Functional/EntityListenersTest.php
new file mode 100644
index 000000000..584795263
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Functional/EntityListenersTest.php
@@ -0,0 +1,247 @@
+useModelSet('company');
+ parent::setUp();
+
+ $this->listener = $this->_em->getConfiguration()
+ ->getEntityListenerResolver()
+ ->resolve('Doctrine\Tests\Models\Company\CompanyContractListener');
+ }
+
+ public function testPreFlushListeners()
+ {
+ $fix = new CompanyFixContract();
+ $fix->setFixPrice(2000);
+
+ $this->listener->preFlushCalls = array();
+
+ $this->_em->persist($fix);
+ $this->_em->flush();
+
+ $this->assertCount(1,$this->listener->preFlushCalls);
+
+ $this->assertSame($fix, $this->listener->preFlushCalls[0][0]);
+
+ $this->assertInstanceOf(
+ 'Doctrine\Tests\Models\Company\CompanyFixContract',
+ $this->listener->preFlushCalls[0][0]
+ );
+
+ $this->assertInstanceOf(
+ 'Doctrine\ORM\Event\PreFlushEventArgs',
+ $this->listener->preFlushCalls[0][1]
+ );
+ }
+
+ public function testPostLoadListeners()
+ {
+ $fix = new CompanyFixContract();
+ $fix->setFixPrice(2000);
+
+ $this->_em->persist($fix);
+ $this->_em->flush();
+ $this->_em->clear();
+
+ $this->listener->postLoadCalls = array();
+
+ $dql = "SELECT f FROM Doctrine\Tests\Models\Company\CompanyFixContract f WHERE f.id = ?1";
+ $fix = $this->_em->createQuery($dql)->setParameter(1, $fix->getId())->getSingleResult();
+
+ $this->assertCount(1,$this->listener->postLoadCalls);
+
+ $this->assertSame($fix, $this->listener->postLoadCalls[0][0]);
+
+ $this->assertInstanceOf(
+ 'Doctrine\Tests\Models\Company\CompanyFixContract',
+ $this->listener->postLoadCalls[0][0]
+ );
+
+ $this->assertInstanceOf(
+ 'Doctrine\ORM\Event\LifecycleEventArgs',
+ $this->listener->postLoadCalls[0][1]
+ );
+ }
+
+ public function testPrePersistListeners()
+ {
+ $fix = new CompanyFixContract();
+ $fix->setFixPrice(2000);
+
+ $this->listener->prePersistCalls = array();
+
+ $this->_em->persist($fix);
+ $this->_em->flush();
+
+ $this->assertCount(1,$this->listener->prePersistCalls);
+
+ $this->assertSame($fix, $this->listener->prePersistCalls[0][0]);
+
+ $this->assertInstanceOf(
+ 'Doctrine\Tests\Models\Company\CompanyFixContract',
+ $this->listener->prePersistCalls[0][0]
+ );
+
+ $this->assertInstanceOf(
+ 'Doctrine\ORM\Event\LifecycleEventArgs',
+ $this->listener->prePersistCalls[0][1]
+ );
+ }
+
+ public function testPostPersistListeners()
+ {
+ $fix = new CompanyFixContract();
+ $fix->setFixPrice(2000);
+
+ $this->listener->postPersistCalls = array();
+
+ $this->_em->persist($fix);
+ $this->_em->flush();
+
+ $this->assertCount(1,$this->listener->postPersistCalls);
+
+ $this->assertSame($fix, $this->listener->postPersistCalls[0][0]);
+
+ $this->assertInstanceOf(
+ 'Doctrine\Tests\Models\Company\CompanyFixContract',
+ $this->listener->postPersistCalls[0][0]
+ );
+
+ $this->assertInstanceOf(
+ 'Doctrine\ORM\Event\LifecycleEventArgs',
+ $this->listener->postPersistCalls[0][1]
+ );
+ }
+
+ public function testPreUpdateListeners()
+ {
+ $fix = new CompanyFixContract();
+ $fix->setFixPrice(1000);
+
+ $this->_em->persist($fix);
+ $this->_em->flush();
+
+ $this->listener->preUpdateCalls = array();
+
+ $fix->setFixPrice(2000);
+
+ $this->_em->persist($fix);
+ $this->_em->flush();
+
+ $this->assertCount(1,$this->listener->preUpdateCalls);
+
+ $this->assertSame($fix, $this->listener->preUpdateCalls[0][0]);
+
+ $this->assertInstanceOf(
+ 'Doctrine\Tests\Models\Company\CompanyFixContract',
+ $this->listener->preUpdateCalls[0][0]
+ );
+
+ $this->assertInstanceOf(
+ 'Doctrine\ORM\Event\PreUpdateEventArgs',
+ $this->listener->preUpdateCalls[0][1]
+ );
+ }
+
+ public function testPostUpdateListeners()
+ {
+ $fix = new CompanyFixContract();
+ $fix->setFixPrice(1000);
+
+ $this->_em->persist($fix);
+ $this->_em->flush();
+
+ $this->listener->postUpdateCalls = array();
+
+ $fix->setFixPrice(2000);
+
+ $this->_em->persist($fix);
+ $this->_em->flush();
+
+ $this->assertCount(1,$this->listener->postUpdateCalls);
+
+ $this->assertSame($fix, $this->listener->postUpdateCalls[0][0]);
+
+ $this->assertInstanceOf(
+ 'Doctrine\Tests\Models\Company\CompanyFixContract',
+ $this->listener->postUpdateCalls[0][0]
+ );
+
+ $this->assertInstanceOf(
+ 'Doctrine\ORM\Event\LifecycleEventArgs',
+ $this->listener->postUpdateCalls[0][1]
+ );
+ }
+
+ public function testPreRemoveListeners()
+ {
+ $fix = new CompanyFixContract();
+ $fix->setFixPrice(1000);
+
+ $this->_em->persist($fix);
+ $this->_em->flush();
+
+ $this->listener->preRemoveCalls = array();
+
+ $this->_em->remove($fix);
+ $this->_em->flush();
+
+ $this->assertCount(1,$this->listener->preRemoveCalls);
+
+ $this->assertSame($fix, $this->listener->preRemoveCalls[0][0]);
+
+ $this->assertInstanceOf(
+ 'Doctrine\Tests\Models\Company\CompanyFixContract',
+ $this->listener->preRemoveCalls[0][0]
+ );
+
+ $this->assertInstanceOf(
+ 'Doctrine\ORM\Event\LifecycleEventArgs',
+ $this->listener->preRemoveCalls[0][1]
+ );
+ }
+
+ public function testPostRemoveListeners()
+ {
+ $fix = new CompanyFixContract();
+ $fix->setFixPrice(1000);
+
+ $this->_em->persist($fix);
+ $this->_em->flush();
+
+ $this->listener->postRemoveCalls = array();
+
+ $this->_em->remove($fix);
+ $this->_em->flush();
+
+ $this->assertCount(1,$this->listener->postRemoveCalls);
+
+ $this->assertSame($fix, $this->listener->postRemoveCalls[0][0]);
+
+ $this->assertInstanceOf(
+ 'Doctrine\Tests\Models\Company\CompanyFixContract',
+ $this->listener->postRemoveCalls[0][0]
+ );
+
+ $this->assertInstanceOf(
+ 'Doctrine\ORM\Event\LifecycleEventArgs',
+ $this->listener->postRemoveCalls[0][1]
+ );
+ }
+}
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php b/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php
index 230f562c6..c19675b6d 100644
--- a/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php
+++ b/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php
@@ -11,6 +11,7 @@ class LifecycleCallbackTest extends \Doctrine\Tests\OrmFunctionalTestCase
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
+ $this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\LifecycleCallbackEventArgEntity'),
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\LifecycleCallbackTestEntity'),
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\LifecycleCallbackTestUser'),
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\LifecycleCallbackCascader'),
@@ -182,6 +183,77 @@ class LifecycleCallbackTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals('Bob', $bob->getName());
}
+
+ /**
+ * @group DDC-1955
+ */
+ public function testLifecycleCallbackEventArgs()
+ {
+ $e = new LifecycleCallbackEventArgEntity;
+
+ $e->value = 'foo';
+ $this->_em->persist($e);
+ $this->_em->flush();
+
+ $e->value = 'var';
+ $this->_em->persist($e);
+ $this->_em->flush();
+
+ $this->_em->refresh($e);
+
+ $this->_em->remove($e);
+ $this->_em->flush();
+
+
+ $this->assertArrayHasKey('preFlushHandler', $e->calls);
+ $this->assertArrayHasKey('postLoadHandler', $e->calls);
+ $this->assertArrayHasKey('prePersistHandler', $e->calls);
+ $this->assertArrayHasKey('postPersistHandler', $e->calls);
+ $this->assertArrayHasKey('preUpdateHandler', $e->calls);
+ $this->assertArrayHasKey('postUpdateHandler', $e->calls);
+ $this->assertArrayHasKey('preRemoveHandler', $e->calls);
+ $this->assertArrayHasKey('postRemoveHandler', $e->calls);
+
+ $this->assertInstanceOf(
+ 'Doctrine\ORM\Event\PreFlushEventArgs',
+ $e->calls['preFlushHandler']
+ );
+
+ $this->assertInstanceOf(
+ 'Doctrine\ORM\Event\LifecycleEventArgs',
+ $e->calls['postLoadHandler']
+ );
+
+ $this->assertInstanceOf(
+ 'Doctrine\ORM\Event\LifecycleEventArgs',
+ $e->calls['prePersistHandler']
+ );
+
+ $this->assertInstanceOf(
+ 'Doctrine\ORM\Event\LifecycleEventArgs',
+ $e->calls['postPersistHandler']
+ );
+
+ $this->assertInstanceOf(
+ 'Doctrine\ORM\Event\PreUpdateEventArgs',
+ $e->calls['preUpdateHandler']
+ );
+
+ $this->assertInstanceOf(
+ 'Doctrine\ORM\Event\LifecycleEventArgs',
+ $e->calls['postUpdateHandler']
+ );
+
+ $this->assertInstanceOf(
+ 'Doctrine\ORM\Event\LifecycleEventArgs',
+ $e->calls['preRemoveHandler']
+ );
+
+ $this->assertInstanceOf(
+ 'Doctrine\ORM\Event\LifecycleEventArgs',
+ $e->calls['postRemoveHandler']
+ );
+ }
}
/** @Entity @HasLifecycleCallbacks */
@@ -309,3 +381,79 @@ class LifecycleListenerPreUpdate
$eventArgs->setNewValue('name', 'Bob');
}
}
+
+/** @Entity @HasLifecycleCallbacks */
+class LifecycleCallbackEventArgEntity
+{
+ /** @Id @Column(type="integer") @GeneratedValue */
+ public $id;
+
+ /** @Column() */
+ public $value;
+
+ public $calls = array();
+
+ /**
+ * @PostPersist
+ */
+ public function postPersistHandler(\Doctrine\ORM\Event\LifecycleEventArgs $event)
+ {
+ $this->calls[__FUNCTION__] = $event;
+ }
+
+ /**
+ * @PrePersist
+ */
+ public function prePersistHandler(\Doctrine\ORM\Event\LifecycleEventArgs $event)
+ {
+ $this->calls[__FUNCTION__] = $event;
+ }
+
+ /**
+ * @PostUpdate
+ */
+ public function postUpdateHandler(\Doctrine\ORM\Event\LifecycleEventArgs $event)
+ {
+ $this->calls[__FUNCTION__] = $event;
+ }
+
+ /**
+ * @PreUpdate
+ */
+ public function preUpdateHandler(\Doctrine\ORM\Event\PreUpdateEventArgs $event)
+ {
+ $this->calls[__FUNCTION__] = $event;
+ }
+
+ /**
+ * @PostRemove
+ */
+ public function postRemoveHandler(\Doctrine\ORM\Event\LifecycleEventArgs $event)
+ {
+ $this->calls[__FUNCTION__] = $event;
+ }
+
+ /**
+ * @PreRemove
+ */
+ public function preRemoveHandler(\Doctrine\ORM\Event\LifecycleEventArgs $event)
+ {
+ $this->calls[__FUNCTION__] = $event;
+ }
+
+ /**
+ * @PreFlush
+ */
+ public function preFlushHandler(\Doctrine\ORM\Event\PreFlushEventArgs $event)
+ {
+ $this->calls[__FUNCTION__] = $event;
+ }
+
+ /**
+ * @PostLoad
+ */
+ public function postLoadHandler(\Doctrine\ORM\Event\LifecycleEventArgs $event)
+ {
+ $this->calls[__FUNCTION__] = $event;
+ }
+}
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1707Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1707Test.php
index 50e702d99..c6c23b991 100644
--- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1707Test.php
+++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1707Test.php
@@ -2,7 +2,7 @@
namespace Doctrine\Tests\ORM\Functional\Ticket;
-use Doctrine\ORM\UnitOfWork;
+use Doctrine\ORM\Event\LifecycleEventArgs;
/**
* @group DDC-1707
@@ -25,9 +25,11 @@ class DDC1707Test extends \Doctrine\Tests\OrmFunctionalTestCase
public function testPostLoadOnChild()
{
- $class = $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1707Child');
+ $class = $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1707Child');
$entity = new DDC1707Child();
- $class->invokeLifecycleCallbacks(\Doctrine\ORM\Events::postLoad, $entity);
+ $event = new LifecycleEventArgs($entity, $this->_em);
+
+ $class->invokeLifecycleCallbacks(\Doctrine\ORM\Events::postLoad, $entity, $event);
$this->assertTrue($entity->postLoad);
}
diff --git a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php
index f752c292e..24b72aaa3 100644
--- a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php
+++ b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php
@@ -2,10 +2,13 @@
namespace Doctrine\Tests\ORM\Mapping;
-use Doctrine\ORM\Mapping\ClassMetadata,
- Doctrine\ORM\Mapping\ClassMetadataInfo,
- Doctrine\ORM\Mapping\Driver\XmlDriver,
- Doctrine\ORM\Mapping\Driver\YamlDriver;
+use Doctrine\ORM\Events;
+use Doctrine\ORM\Event\LifecycleEventArgs;
+use Doctrine\Tests\Models\Company\CompanyFixContract;
+use Doctrine\Tests\Models\Company\CompanyFlexContract;
+
+use Doctrine\ORM\Mapping\ClassMetadata;
+use Doctrine\ORM\Mapping\ClassMetadataInfo;
require_once __DIR__ . '/../../TestInit.php';
@@ -748,6 +751,126 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
$this->assertTrue($guestMetadata->fieldMappings['name']['unique']);
}
+ /**
+ * @group DDC-1955
+ */
+ public function testEntityListeners()
+ {
+ $em = $this->_getTestEntityManager();
+ $factory = $this->createClassMetadataFactory($em);
+ $superClass = $factory->getMetadataFor('Doctrine\Tests\Models\Company\CompanyContract');
+ $flexClass = $factory->getMetadataFor('Doctrine\Tests\Models\Company\CompanyFixContract');
+ $fixClass = $factory->getMetadataFor('Doctrine\Tests\Models\Company\CompanyFlexContract');
+ $ultraClass = $factory->getMetadataFor('Doctrine\Tests\Models\Company\CompanyFlexUltraContract');
+
+ $this->assertArrayHasKey(Events::prePersist, $superClass->entityListeners);
+ $this->assertArrayHasKey(Events::postPersist, $superClass->entityListeners);
+
+ $this->assertCount(1, $superClass->entityListeners[Events::prePersist]);
+ $this->assertCount(1, $superClass->entityListeners[Events::postPersist]);
+
+ $postPersist = $superClass->entityListeners[Events::postPersist][0];
+ $prePersist = $superClass->entityListeners[Events::prePersist][0];
+
+ $this->assertEquals('Doctrine\Tests\Models\Company\CompanyContractListener', $postPersist['class']);
+ $this->assertEquals('Doctrine\Tests\Models\Company\CompanyContractListener', $prePersist['class']);
+ $this->assertEquals('postPersistHandler', $postPersist['method']);
+ $this->assertEquals('prePersistHandler', $prePersist['method']);
+
+ //Inherited listeners
+ $this->assertEquals($fixClass->entityListeners, $superClass->entityListeners);
+ $this->assertEquals($flexClass->entityListeners, $superClass->entityListeners);
+ }
+
+ /**
+ * @group DDC-1955
+ */
+ public function testEntityListenersOverride()
+ {
+ $em = $this->_getTestEntityManager();
+ $factory = $this->createClassMetadataFactory($em);
+ $ultraClass = $factory->getMetadataFor('Doctrine\Tests\Models\Company\CompanyFlexUltraContract');
+
+ //overrited listeners
+ $this->assertArrayHasKey(Events::postPersist, $ultraClass->entityListeners);
+ $this->assertArrayHasKey(Events::prePersist, $ultraClass->entityListeners);
+
+ $this->assertCount(1, $ultraClass->entityListeners[Events::postPersist]);
+ $this->assertCount(3, $ultraClass->entityListeners[Events::prePersist]);
+
+ $postPersist = $ultraClass->entityListeners[Events::postPersist][0];
+ $prePersist = $ultraClass->entityListeners[Events::prePersist][0];
+
+ $this->assertEquals('Doctrine\Tests\Models\Company\CompanyContractListener', $postPersist['class']);
+ $this->assertEquals('Doctrine\Tests\Models\Company\CompanyContractListener', $prePersist['class']);
+ $this->assertEquals('postPersistHandler', $postPersist['method']);
+ $this->assertEquals('prePersistHandler', $prePersist['method']);
+
+ $prePersist = $ultraClass->entityListeners[Events::prePersist][1];
+ $this->assertEquals('Doctrine\Tests\Models\Company\CompanyFlexUltraContractListener', $prePersist['class']);
+ $this->assertEquals('prePersistHandler1', $prePersist['method']);
+
+ $prePersist = $ultraClass->entityListeners[Events::prePersist][2];
+ $this->assertEquals('Doctrine\Tests\Models\Company\CompanyFlexUltraContractListener', $prePersist['class']);
+ $this->assertEquals('prePersistHandler2', $prePersist['method']);
+ }
+
+
+ /**
+ * @group DDC-1955
+ */
+ public function testEntityListenersNamingConvention()
+ {
+ $em = $this->_getTestEntityManager();
+ $factory = $this->createClassMetadataFactory($em);
+ $metadata = $factory->getMetadataFor('Doctrine\Tests\Models\CMS\CmsAddress');
+
+ $this->assertArrayHasKey(Events::postPersist, $metadata->entityListeners);
+ $this->assertArrayHasKey(Events::prePersist, $metadata->entityListeners);
+ $this->assertArrayHasKey(Events::postUpdate, $metadata->entityListeners);
+ $this->assertArrayHasKey(Events::preUpdate, $metadata->entityListeners);
+ $this->assertArrayHasKey(Events::postRemove, $metadata->entityListeners);
+ $this->assertArrayHasKey(Events::preRemove, $metadata->entityListeners);
+ $this->assertArrayHasKey(Events::postLoad, $metadata->entityListeners);
+ $this->assertArrayHasKey(Events::preFlush, $metadata->entityListeners);
+
+ $this->assertCount(1, $metadata->entityListeners[Events::postPersist]);
+ $this->assertCount(1, $metadata->entityListeners[Events::prePersist]);
+ $this->assertCount(1, $metadata->entityListeners[Events::postUpdate]);
+ $this->assertCount(1, $metadata->entityListeners[Events::preUpdate]);
+ $this->assertCount(1, $metadata->entityListeners[Events::postRemove]);
+ $this->assertCount(1, $metadata->entityListeners[Events::preRemove]);
+ $this->assertCount(1, $metadata->entityListeners[Events::postLoad]);
+ $this->assertCount(1, $metadata->entityListeners[Events::preFlush]);
+
+ $postPersist = $metadata->entityListeners[Events::postPersist][0];
+ $prePersist = $metadata->entityListeners[Events::prePersist][0];
+ $postUpdate = $metadata->entityListeners[Events::postUpdate][0];
+ $preUpdate = $metadata->entityListeners[Events::preUpdate][0];
+ $postRemove = $metadata->entityListeners[Events::postRemove][0];
+ $preRemove = $metadata->entityListeners[Events::preRemove][0];
+ $postLoad = $metadata->entityListeners[Events::postLoad][0];
+ $preFlush = $metadata->entityListeners[Events::preFlush][0];
+
+
+ $this->assertEquals('Doctrine\Tests\Models\CMS\CmsAddressListener', $postPersist['class']);
+ $this->assertEquals('Doctrine\Tests\Models\CMS\CmsAddressListener', $prePersist['class']);
+ $this->assertEquals('Doctrine\Tests\Models\CMS\CmsAddressListener', $postUpdate['class']);
+ $this->assertEquals('Doctrine\Tests\Models\CMS\CmsAddressListener', $preUpdate['class']);
+ $this->assertEquals('Doctrine\Tests\Models\CMS\CmsAddressListener', $postRemove['class']);
+ $this->assertEquals('Doctrine\Tests\Models\CMS\CmsAddressListener', $preRemove['class']);
+ $this->assertEquals('Doctrine\Tests\Models\CMS\CmsAddressListener', $postLoad['class']);
+ $this->assertEquals('Doctrine\Tests\Models\CMS\CmsAddressListener', $preFlush['class']);
+
+ $this->assertEquals(Events::postPersist, $postPersist['method']);
+ $this->assertEquals(Events::prePersist, $prePersist['method']);
+ $this->assertEquals(Events::postUpdate, $postUpdate['method']);
+ $this->assertEquals(Events::preUpdate, $preUpdate['method']);
+ $this->assertEquals(Events::postRemove, $postRemove['method']);
+ $this->assertEquals(Events::preRemove, $preRemove['method']);
+ $this->assertEquals(Events::postLoad, $postLoad['method']);
+ $this->assertEquals(Events::preFlush, $preFlush['method']);
+ }
}
/**
diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php
index fee6d822c..41f2ffa2f 100644
--- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php
+++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php
@@ -997,6 +997,34 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
$cm->setAttributeOverride('name', array('type'=>'date'));
}
+
+ /**
+ * @group DDC-1955
+ *
+ * @expectedException Doctrine\ORM\Mapping\MappingException
+ * @expectedExceptionMessage Entity Listener "\InvalidClassName" declared on "Doctrine\Tests\Models\CMS\CmsUser" not found.
+ */
+ public function testInvalidEntityListenerClassException()
+ {
+ $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
+ $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
+
+ $cm->addEntityListener(Events::postLoad, '\InvalidClassName', 'postLoadHandler');
+ }
+
+ /**
+ * @group DDC-1955
+ *
+ * @expectedException Doctrine\ORM\Mapping\MappingException
+ * @expectedExceptionMessage Entity Listener "\Doctrine\Tests\Models\Company\CompanyContractListener" declared on "Doctrine\Tests\Models\CMS\CmsUser" has no method "invalidMethod".
+ */
+ public function testInvalidEntityListenerMethodException()
+ {
+ $cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
+ $cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
+
+ $cm->addEntityListener(Events::postLoad, '\Doctrine\Tests\Models\Company\CompanyContractListener', 'invalidMethod');
+ }
}
class MyNamespacedNamingStrategy extends \Doctrine\ORM\Mapping\DefaultNamingStrategy
diff --git a/tests/Doctrine/Tests/ORM/Mapping/EntityListenerResolverTest.php b/tests/Doctrine/Tests/ORM/Mapping/EntityListenerResolverTest.php
new file mode 100644
index 000000000..64007b031
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Mapping/EntityListenerResolverTest.php
@@ -0,0 +1,97 @@
+resolver = new DefaultEntityListenerResolver();
+ }
+
+ public function testResolve()
+ {
+ $className = '\Doctrine\Tests\Models\Company\CompanyContractListener';
+ $object = $this->resolver->resolve($className);
+
+ $this->assertInstanceOf($className, $object);
+ $this->assertSame($object, $this->resolver->resolve($className));
+ }
+
+ public function testRegisterAndResolve()
+ {
+ $className = '\Doctrine\Tests\Models\Company\CompanyContractListener';
+ $object = new $className();
+
+ $this->resolver->register($object);
+
+ $this->assertSame($object, $this->resolver->resolve($className));
+ }
+
+ public function testClearOne()
+ {
+ $className1 = '\Doctrine\Tests\Models\Company\CompanyContractListener';
+ $className2 = '\Doctrine\Tests\Models\Company\CompanyFlexUltraContractListener';
+
+ $obj1 = $this->resolver->resolve($className1);
+ $obj2 = $this->resolver->resolve($className2);
+
+ $this->assertInstanceOf($className1, $obj1);
+ $this->assertInstanceOf($className2, $obj2);
+
+ $this->assertSame($obj1, $this->resolver->resolve($className1));
+ $this->assertSame($obj2, $this->resolver->resolve($className2));
+
+ $this->resolver->clear($className1);
+
+ $this->assertInstanceOf($className1, $this->resolver->resolve($className1));
+ $this->assertInstanceOf($className2, $this->resolver->resolve($className2));
+
+ $this->assertNotSame($obj1, $this->resolver->resolve($className1));
+ $this->assertSame($obj2, $this->resolver->resolve($className2));
+ }
+
+ public function testClearAll()
+ {
+ $className1 = '\Doctrine\Tests\Models\Company\CompanyContractListener';
+ $className2 = '\Doctrine\Tests\Models\Company\CompanyFlexUltraContractListener';
+
+ $obj1 = $this->resolver->resolve($className1);
+ $obj2 = $this->resolver->resolve($className2);
+
+ $this->assertInstanceOf($className1, $obj1);
+ $this->assertInstanceOf($className2, $obj2);
+
+ $this->assertSame($obj1, $this->resolver->resolve($className1));
+ $this->assertSame($obj2, $this->resolver->resolve($className2));
+
+ $this->resolver->clear();
+
+ $this->assertInstanceOf($className1, $this->resolver->resolve($className1));
+ $this->assertInstanceOf($className2, $this->resolver->resolve($className2));
+
+ $this->assertNotSame($obj1, $this->resolver->resolve($className1));
+ $this->assertNotSame($obj2, $this->resolver->resolve($className2));
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ * @expectedExceptionMessage An object was expected, but got "string".
+ */
+ public function testRegisterStringException()
+ {
+ $this->resolver->register('CompanyContractListener');
+ }
+}
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.CMS.CmsAddress.php b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.CMS.CmsAddress.php
index 964065cbd..24739a50c 100644
--- a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.CMS.CmsAddress.php
+++ b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.CMS.CmsAddress.php
@@ -6,6 +6,27 @@ $metadata->setPrimaryTable(array(
'name' => 'company_person',
));
+$metadata->mapField(array (
+ 'id' => true,
+ 'fieldName' => 'id',
+ 'type' => 'integer',
+));
+
+$metadata->mapField(array (
+ 'fieldName' => 'zip',
+ 'length' => 50,
+));
+
+$metadata->mapField(array (
+ 'fieldName' => 'city',
+ 'length' => 50,
+));
+
+$metadata->mapOneToOne(array(
+ 'fieldName' => 'user',
+ 'targetEntity' => 'CmsUser',
+ 'joinColumns' => array(array('referencedColumnName' => 'id'))
+));
$metadata->addNamedNativeQuery(array (
'name' => 'find-all',
@@ -66,4 +87,16 @@ $metadata->addSqlResultSetMapping(array (
'name' => 'count',
),
)
-));
\ No newline at end of file
+));
+
+$metadata->addEntityListener(\Doctrine\ORM\Events::postPersist, 'CmsAddressListener', 'postPersist');
+$metadata->addEntityListener(\Doctrine\ORM\Events::prePersist, 'CmsAddressListener', 'prePersist');
+
+$metadata->addEntityListener(\Doctrine\ORM\Events::postUpdate, 'CmsAddressListener', 'postUpdate');
+$metadata->addEntityListener(\Doctrine\ORM\Events::preUpdate, 'CmsAddressListener', 'preUpdate');
+
+$metadata->addEntityListener(\Doctrine\ORM\Events::postRemove, 'CmsAddressListener', 'postRemove');
+$metadata->addEntityListener(\Doctrine\ORM\Events::preRemove, 'CmsAddressListener', 'preRemove');
+
+$metadata->addEntityListener(\Doctrine\ORM\Events::preFlush, 'CmsAddressListener', 'preFlush');
+$metadata->addEntityListener(\Doctrine\ORM\Events::postLoad, 'CmsAddressListener', 'postLoad');
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyContract.php b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyContract.php
new file mode 100644
index 000000000..fdb8d5d02
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyContract.php
@@ -0,0 +1,40 @@
+setInheritanceType(\Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_JOINED);
+$metadata->setTableName( 'company_contracts');
+$metadata->setDiscriminatorColumn(array(
+ 'name' => 'discr',
+ 'type' => 'string',
+));
+
+$metadata->mapField(array(
+ 'id' => true,
+ 'name' => 'id',
+ 'fieldName' => 'id',
+));
+
+$metadata->mapField(array(
+ 'type' => 'boolean',
+ 'name' => 'completed',
+ 'fieldName' => 'completed',
+));
+
+$metadata->setDiscriminatorMap(array(
+ "fix" => "CompanyFixContract",
+ "flexible" => "CompanyFlexContract",
+ "flexultra" => "CompanyFlexUltraContract"
+));
+
+$metadata->addEntityListener(\Doctrine\ORM\Events::postPersist, 'CompanyContractListener', 'postPersistHandler');
+$metadata->addEntityListener(\Doctrine\ORM\Events::prePersist, 'CompanyContractListener', 'prePersistHandler');
+
+$metadata->addEntityListener(\Doctrine\ORM\Events::postUpdate, 'CompanyContractListener', 'postUpdateHandler');
+$metadata->addEntityListener(\Doctrine\ORM\Events::preUpdate, 'CompanyContractListener', 'preUpdateHandler');
+
+$metadata->addEntityListener(\Doctrine\ORM\Events::postRemove, 'CompanyContractListener', 'postRemoveHandler');
+$metadata->addEntityListener(\Doctrine\ORM\Events::preRemove, 'CompanyContractListener', 'preRemoveHandler');
+
+$metadata->addEntityListener(\Doctrine\ORM\Events::preFlush, 'CompanyContractListener', 'preFlushHandler');
+$metadata->addEntityListener(\Doctrine\ORM\Events::postLoad, 'CompanyContractListener', 'postLoadHandler');
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyFixContract.php b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyFixContract.php
new file mode 100644
index 000000000..79b117729
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyFixContract.php
@@ -0,0 +1,7 @@
+mapField(array(
+ 'type' => 'integer',
+ 'name' => 'fixPrice',
+ 'fieldName' => 'fixPrice',
+));
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyFlexContract.php b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyFlexContract.php
new file mode 100644
index 000000000..5f1542dde
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyFlexContract.php
@@ -0,0 +1,13 @@
+mapField(array(
+ 'type' => 'integer',
+ 'name' => 'hoursWorked',
+ 'fieldName' => 'hoursWorked',
+));
+
+$metadata->mapField(array(
+ 'type' => 'integer',
+ 'name' => 'pricePerHour',
+ 'fieldName' => 'pricePerHour',
+));
diff --git a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyFlexUltraContract.php b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyFlexUltraContract.php
new file mode 100644
index 000000000..d16d4f3f5
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.Models.Company.CompanyFlexUltraContract.php
@@ -0,0 +1,21 @@
+mapField(array(
+ 'type' => 'integer',
+ 'name' => 'maxPrice',
+ 'fieldName' => 'maxPrice',
+));
+$metadata->addEntityListener(\Doctrine\ORM\Events::postPersist, 'CompanyContractListener', 'postPersistHandler');
+$metadata->addEntityListener(\Doctrine\ORM\Events::prePersist, 'CompanyContractListener', 'prePersistHandler');
+
+$metadata->addEntityListener(\Doctrine\ORM\Events::postUpdate, 'CompanyContractListener', 'postUpdateHandler');
+$metadata->addEntityListener(\Doctrine\ORM\Events::preUpdate, 'CompanyContractListener', 'preUpdateHandler');
+
+$metadata->addEntityListener(\Doctrine\ORM\Events::postRemove, 'CompanyContractListener', 'postRemoveHandler');
+$metadata->addEntityListener(\Doctrine\ORM\Events::preRemove, 'CompanyContractListener', 'preRemoveHandler');
+
+$metadata->addEntityListener(\Doctrine\ORM\Events::preFlush, 'CompanyContractListener', 'preFlushHandler');
+$metadata->addEntityListener(\Doctrine\ORM\Events::postLoad, 'CompanyContractListener', 'postLoadHandler');
+
+$metadata->addEntityListener(\Doctrine\ORM\Events::prePersist, 'CompanyFlexUltraContractListener', 'prePersistHandler1');
+$metadata->addEntityListener(\Doctrine\ORM\Events::prePersist, 'CompanyFlexUltraContractListener', 'prePersistHandler2');
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.xml
index 0af5facda..b35569ae4 100644
--- a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.xml
+++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.xml
@@ -7,6 +7,10 @@
+
+
+
+
SELECT id, country, city FROM cms_addresses
diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyContract.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyContract.dcm.xml
new file mode 100644
index 000000000..4fa0c23e1
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyContract.dcm.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyFixContract.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyFixContract.dcm.xml
new file mode 100644
index 000000000..bd1019d68
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyFixContract.dcm.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyFlexContract.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyFlexContract.dcm.xml
new file mode 100644
index 000000000..91ef0bb1c
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyFlexContract.dcm.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyFlexUltraContract.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyFlexUltraContract.dcm.xml
new file mode 100644
index 000000000..729bdfda0
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Company.CompanyFlexUltraContract.dcm.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.yml
index 604acb293..da2afca75 100644
--- a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.yml
+++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.CMS.CmsAddress.dcm.yml
@@ -1,6 +1,8 @@
Doctrine\Tests\Models\CMS\CmsAddress:
type: entity
table: cms_address
+ entityListeners:
+ CmsAddressListener:
namedNativeQueries:
find-all:
resultSetMapping: mapping-find-all
@@ -55,7 +57,7 @@ Doctrine\Tests\Models\CMS\CmsAddress:
type: string
length: 50
oneToOne:
- address:
+ user:
targetEntity: CmsUser
inversedBy: address
joinColumn:
diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyContract.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyContract.dcm.yml
new file mode 100644
index 000000000..03b9d3bed
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyContract.dcm.yml
@@ -0,0 +1,32 @@
+Doctrine\Tests\Models\Company\CompanyContract:
+ type: entity
+ table: company_contracts
+ inheritanceType: SINGLE_TABLE
+ discriminatorMap:
+ fix: CompanyFixContract
+ flexible: CompanyFlexContract
+ flexultra: CompanyFlexUltraContract
+
+ entityListeners:
+ CompanyContractListener:
+
+ preFlush: [preFlushHandler]
+ postLoad: [postLoadHandler]
+
+ postPersist: [postPersistHandler]
+ prePersist: [prePersistHandler]
+
+ postUpdate: [postUpdateHandler]
+ preUpdate: [preUpdateHandler]
+
+ postRemove: [postRemoveHandler]
+ preRemove: [preRemoveHandler]
+
+ id:
+ id:
+ type: integer
+ generator:
+ strategy: AUTO
+ fields:
+ completed:
+ type: boolean
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyFixContract.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyFixContract.dcm.yml
new file mode 100644
index 000000000..83d0c75be
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyFixContract.dcm.yml
@@ -0,0 +1,5 @@
+Doctrine\Tests\Models\Company\CompanyFixContract:
+ type: entity
+ fields:
+ fixPrice:
+ type: integer
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyFlexContract.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyFlexContract.dcm.yml
new file mode 100644
index 000000000..ef1d26306
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyFlexContract.dcm.yml
@@ -0,0 +1,7 @@
+Doctrine\Tests\Models\Company\CompanyFlexContract:
+ type: entity
+ fields:
+ hoursWorked:
+ type: integer
+ pricePerHour:
+ type: integer
\ No newline at end of file
diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyFlexUltraContract.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyFlexUltraContract.dcm.yml
new file mode 100644
index 000000000..26ce8f3d5
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.Models.Company.CompanyFlexUltraContract.dcm.yml
@@ -0,0 +1,25 @@
+Doctrine\Tests\Models\Company\CompanyFlexUltraContract:
+ type: entity
+
+ entityListeners:
+ CompanyContractListener:
+
+ preFlush: [preFlushHandler]
+ postLoad: [postLoadHandler]
+
+ postPersist: [postPersistHandler]
+ prePersist: [prePersistHandler]
+
+ postUpdate: [postUpdateHandler]
+ preUpdate: [preUpdateHandler]
+
+ postRemove: [postRemoveHandler]
+ preRemove: [preRemoveHandler]
+
+ CompanyFlexUltraContractListener:
+
+ prePersist: [prePersistHandler1, prePersistHandler2]
+
+ fields:
+ maxPrice:
+ type: integer
\ No newline at end of file