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