From 51528fbdeaf36dcec8c05b09066aa1fa40268a9b Mon Sep 17 00:00:00 2001 From: Bart van den Burg Date: Fri, 29 Jun 2012 12:18:37 +0200 Subject: [PATCH 01/23] set metadata for interface to be able to fetch entites by interface name --- lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php | 6 ++++++ .../Tests/ORM/Tools/ResolveTargetEntityListenerTest.php | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php b/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php index e193ae2e3..f0d3ce25f 100644 --- a/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php +++ b/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php @@ -69,6 +69,12 @@ class ResolveTargetEntityListener $this->remapAssociation($cm, $mapping); } } + + foreach ($this->resolveTargetEntities as $interface => $data) { + if ($data['targetEntity'] == $cm->getName()) { + $args->getEntityManager()->getMetadataFactory()->setMetadataFor($interface, $cm); + } + } } /** diff --git a/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php b/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php index 3b4219623..fb0e301e0 100644 --- a/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php @@ -29,8 +29,7 @@ class ResolveTargetEntityListenerTest extends \Doctrine\Tests\OrmTestCase $this->em = $this->_getTestEntityManager(); $this->em->getConfiguration()->setMetadataDriverImpl($annotationDriver); - $this->factory = new ClassMetadataFactory; - $this->factory->setEntityManager($this->em); + $this->factory = $this->em->getMetadataFactory(); $this->listener = new ResolveTargetEntityListener; } @@ -57,6 +56,8 @@ class ResolveTargetEntityListenerTest extends \Doctrine\Tests\OrmTestCase $this->assertSame('Doctrine\Tests\ORM\Tools\ResolveTargetEntity', $meta['manyToOne']['targetEntity']); $this->assertSame('Doctrine\Tests\ORM\Tools\ResolveTargetEntity', $meta['oneToMany']['targetEntity']); $this->assertSame('Doctrine\Tests\ORM\Tools\TargetEntity', $meta['oneToOne']['targetEntity']); + + $this->assertSame($cm, $this->factory->getMetadataFor('Doctrine\Tests\ORM\Tools\ResolveTargetInterface')); } /** From 19c52e4ae11354e9988abec7336e7375e29186f4 Mon Sep 17 00:00:00 2001 From: Bart van den Burg Date: Fri, 29 Jun 2012 14:57:13 +0200 Subject: [PATCH 02/23] added failing test --- .../Tests/ORM/Tools/ResolveTargetEntityListenerTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php b/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php index fb0e301e0..9b7167a9c 100644 --- a/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php @@ -50,6 +50,9 @@ class ResolveTargetEntityListenerTest extends \Doctrine\Tests\OrmTestCase array() ); $evm->addEventListener(Events::loadClassMetadata, $this->listener); + + $this->assertNotNull($this->factory->getMetadataFor('Doctrine\Tests\ORM\Tools\ResolveTargetInterface')); + $cm = $this->factory->getMetadataFor('Doctrine\Tests\ORM\Tools\ResolveTargetEntity'); $meta = $cm->associationMappings; $this->assertSame('Doctrine\Tests\ORM\Tools\TargetEntity', $meta['manyToMany']['targetEntity']); From 437f81271862cf1edde863c32ed471c08357a482 Mon Sep 17 00:00:00 2001 From: Bart van den Burg Date: Wed, 4 Jul 2012 22:39:08 +0200 Subject: [PATCH 03/23] Added new event to allow actions to be done before trying to load class metadata --- .../Event/PreLoadClassMetadataEventArgs.php | 75 +++++++++++++++++++ lib/Doctrine/ORM/Events.php | 7 ++ .../ORM/Tools/ResolveTargetEntityListener.php | 19 ++++- .../Tools/ResolveTargetEntityListenerTest.php | 5 +- 4 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 lib/Doctrine/ORM/Event/PreLoadClassMetadataEventArgs.php diff --git a/lib/Doctrine/ORM/Event/PreLoadClassMetadataEventArgs.php b/lib/Doctrine/ORM/Event/PreLoadClassMetadataEventArgs.php new file mode 100644 index 000000000..e111cdf59 --- /dev/null +++ b/lib/Doctrine/ORM/Event/PreLoadClassMetadataEventArgs.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\ORM\EntityManager; + +/** + * Class that holds event arguments for a loadMetadata event. + * + * @author Jonathan H. Wage + * @since 2.3 + */ +class PreLoadClassMetadataEventArgs extends EventArgs +{ + /** + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + private $className; + + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * Constructor. + * + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $classMetadata + * @param \Doctrine\ORM\EntityManager $em + */ + public function __construct($className, EntityManager $em) + { + $this->className = $className; + $this->em = $em; + } + + /** + * Retrieve associated ClassMetadata. + * + * @return \Doctrine\ORM\Mapping\ClassMetadataInfo + */ + public function getClassName() + { + return $this->className; + } + + /** + * Retrieve associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } +} + diff --git a/lib/Doctrine/ORM/Events.php b/lib/Doctrine/ORM/Events.php index 8c13fa2d5..e27a4163a 100644 --- a/lib/Doctrine/ORM/Events.php +++ b/lib/Doctrine/ORM/Events.php @@ -119,6 +119,13 @@ final class Events * @var string */ const loadClassMetadata = 'loadClassMetadata'; + /** + * The preLoadClassMetadata event occurs before the mapping metadata for a + * class is loaded from a mapping source (annotations/xml/yaml). + * + * @var string + */ + const preLoadClassMetadata = 'preLoadClassMetadata'; /** * The preFlush event occurs when the EntityManager#flush() operation is invoked, diff --git a/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php b/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php index f0d3ce25f..374dba8ab 100644 --- a/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php +++ b/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php @@ -20,7 +20,10 @@ namespace Doctrine\ORM\Tools; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; +use Doctrine\ORM\Event\PreLoadClassMetadataEventArgs; use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\Common\EventSubscriber; +use Doctrine\ORM\Events; /** * ResolveTargetEntityListener @@ -31,13 +34,20 @@ use Doctrine\ORM\Mapping\ClassMetadata; * @author Benjamin Eberlei * @since 2.2 */ -class ResolveTargetEntityListener +class ResolveTargetEntityListener implements EventSubscriber { /** * @var array */ private $resolveTargetEntities = array(); + public function getSubscribedEvents() + { + return array( + Events::loadClassMetadata, + Events::preLoadClassMetadata + ); + } /** * Adds a target-entity class name to resolve to a new class name. * @@ -53,6 +63,13 @@ class ResolveTargetEntityListener $this->resolveTargetEntities[ltrim($originalEntity, "\\")] = $mapping; } + public function preLoadClassMetadata(PreLoadClassMetadataEventArgs $args) + { + if (array_key_exists($args->getClassName(), $this->resolveTargetEntities)) { + $args->getEntityManager()->getClassMetadata($this->resolveTargetEntities[$args->getClassname()]['targetEntity']); + } + } + /** * Processes event and resolves new target entity names. * diff --git a/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php b/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php index 9b7167a9c..a330e3c2e 100644 --- a/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php @@ -2,7 +2,6 @@ namespace Doctrine\Tests\ORM\Tools; -use Doctrine\ORM\Events; use Doctrine\ORM\Mapping\ClassMetadataFactory; use Doctrine\ORM\Tools\ResolveTargetEntityListener; @@ -30,7 +29,7 @@ class ResolveTargetEntityListenerTest extends \Doctrine\Tests\OrmTestCase $this->em = $this->_getTestEntityManager(); $this->em->getConfiguration()->setMetadataDriverImpl($annotationDriver); $this->factory = $this->em->getMetadataFactory(); - $this->listener = new ResolveTargetEntityListener; + $this->listener = new ResolveTargetEntityListener(); } /** @@ -49,7 +48,7 @@ class ResolveTargetEntityListenerTest extends \Doctrine\Tests\OrmTestCase 'Doctrine\Tests\ORM\Tools\TargetEntity', array() ); - $evm->addEventListener(Events::loadClassMetadata, $this->listener); + $evm->addEventSubscriber($this->listener); $this->assertNotNull($this->factory->getMetadataFor('Doctrine\Tests\ORM\Tools\ResolveTargetInterface')); From 3be43a1defdc5fed915e3c7112279c161581ac95 Mon Sep 17 00:00:00 2001 From: Bart van den Burg Date: Fri, 23 Nov 2012 12:15:30 +0100 Subject: [PATCH 04/23] Rebased and renamed PreLoadClassMetadata to OnClassMetadataNotFound --- ...taEventArgs.php => OnClassMetadataNotFoundEventArgs.php} | 2 +- lib/Doctrine/ORM/Events.php | 2 +- lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) rename lib/Doctrine/ORM/Event/{PreLoadClassMetadataEventArgs.php => OnClassMetadataNotFoundEventArgs.php} (97%) diff --git a/lib/Doctrine/ORM/Event/PreLoadClassMetadataEventArgs.php b/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php similarity index 97% rename from lib/Doctrine/ORM/Event/PreLoadClassMetadataEventArgs.php rename to lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php index e111cdf59..8eac9afc0 100644 --- a/lib/Doctrine/ORM/Event/PreLoadClassMetadataEventArgs.php +++ b/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php @@ -28,7 +28,7 @@ use Doctrine\ORM\EntityManager; * @author Jonathan H. Wage * @since 2.3 */ -class PreLoadClassMetadataEventArgs extends EventArgs +class OnClassMetadataNotFoundEventArgs extends EventArgs { /** * @var \Doctrine\ORM\Mapping\ClassMetadata diff --git a/lib/Doctrine/ORM/Events.php b/lib/Doctrine/ORM/Events.php index e27a4163a..d089a4366 100644 --- a/lib/Doctrine/ORM/Events.php +++ b/lib/Doctrine/ORM/Events.php @@ -125,7 +125,7 @@ final class Events * * @var string */ - const preLoadClassMetadata = 'preLoadClassMetadata'; + const onClassMetadataNotFound = 'onClassMetadataNotFound'; /** * The preFlush event occurs when the EntityManager#flush() operation is invoked, diff --git a/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php b/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php index 374dba8ab..a0f1e0b39 100644 --- a/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php +++ b/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php @@ -20,7 +20,7 @@ namespace Doctrine\ORM\Tools; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; -use Doctrine\ORM\Event\PreLoadClassMetadataEventArgs; +use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\Common\EventSubscriber; use Doctrine\ORM\Events; @@ -45,7 +45,7 @@ class ResolveTargetEntityListener implements EventSubscriber { return array( Events::loadClassMetadata, - Events::preLoadClassMetadata + Events::onClassMetadataNotFound ); } /** @@ -63,7 +63,7 @@ class ResolveTargetEntityListener implements EventSubscriber $this->resolveTargetEntities[ltrim($originalEntity, "\\")] = $mapping; } - public function preLoadClassMetadata(PreLoadClassMetadataEventArgs $args) + public function onClassMetadataNotFound(OnClassMetadataNotFoundEventArgs $args) { if (array_key_exists($args->getClassName(), $this->resolveTargetEntities)) { $args->getEntityManager()->getClassMetadata($this->resolveTargetEntities[$args->getClassname()]['targetEntity']); From d66356542d19e4d97c3dc0475b5c6052101fd5cb Mon Sep 17 00:00:00 2001 From: Bart van den Burg Date: Fri, 23 Nov 2012 16:12:05 +0100 Subject: [PATCH 05/23] added use statement --- .../Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php b/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php index a330e3c2e..cbc6d40a7 100644 --- a/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php @@ -4,6 +4,7 @@ namespace Doctrine\Tests\ORM\Tools; use Doctrine\ORM\Mapping\ClassMetadataFactory; use Doctrine\ORM\Tools\ResolveTargetEntityListener; +use Doctrine\ORM\Events; class ResolveTargetEntityListenerTest extends \Doctrine\Tests\OrmTestCase { From efd4500e6ba4cc388806b5f189b803a692f6a16d Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 11 Nov 2014 15:50:13 +0100 Subject: [PATCH 06/23] `ClassMetadataFactory` should support fallback (event-based) logic for hooking into failed `ClassMetadata` loading --- .../ORM/Mapping/ClassMetadataFactoryTest.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php index e87d4dc51..8ea01fce2 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php @@ -2,6 +2,8 @@ namespace Doctrine\Tests\ORM\Mapping; +use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs; +use Doctrine\ORM\Events; use Doctrine\Tests\Mocks\MetadataDriverMock; use Doctrine\Tests\Mocks\EntityManagerMock; use Doctrine\Tests\Mocks\ConnectionMock; @@ -322,6 +324,35 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals('group-id', $groups['joinTable']['inverseJoinColumns'][0]['referencedColumnName']); } + public function testFallbackLoadingCausesEventTriggeringThatCanModifyFetchedMetadata() + { + $test = $this; + /* @var $metadata \Doctrine\Common\Persistence\Mapping\ClassMetadata */ + $metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + $cmf = new ClassMetadataFactory(); + $mockDriver = new MetadataDriverMock(); + $em = $this->_createEntityManager($mockDriver); + $listener = $this->getMock('stdClass', array('onClassMetadataNotFound')); + $eventManager = $em->getEventManager(); + + $cmf->setEntityManager($em); + + $listener + ->expects($this->any()) + ->method('onClassMetadataNotFound') + ->will($this->returnCallback(function (OnClassMetadataNotFoundEventArgs $args) use ($metadata, $em, $test) { + $test->assertNull($args->getFoundMetadata()); + $test->assertSame('Foo', $args->getClassName()); + $test->assertSame($em, $args->getEntityManager()); + + $args->setFoundMetadata($metadata); + })); + + $eventManager->addEventListener(array(Events::onClassMetadataNotFound), $listener); + + $this->assertSame($metadata, $cmf->getMetadataFor('Foo')); + } + /** * @group DDC-3427 */ From f5eb20b63d030b2c0bcd8b8ab3ebb243b06a383d Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 11 Nov 2014 15:50:55 +0100 Subject: [PATCH 07/23] `OnClassMetadataNotFoundEventArgs` should support setting the actually resolved `ClassMetadata` as a mutable event result vector --- .../OnClassMetadataNotFoundEventArgs.php | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php b/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php index 8eac9afc0..6d7b128ed 100644 --- a/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php +++ b/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php @@ -20,7 +20,9 @@ namespace Doctrine\ORM\Event; use Doctrine\Common\EventArgs; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; use Doctrine\ORM\EntityManager; +use Doctrine\ORM\EntityManagerInterface; /** * Class that holds event arguments for a loadMetadata event. @@ -31,27 +33,48 @@ use Doctrine\ORM\EntityManager; class OnClassMetadataNotFoundEventArgs extends EventArgs { /** - * @var \Doctrine\ORM\Mapping\ClassMetadata + * @var string */ private $className; /** - * @var \Doctrine\ORM\EntityManager + * @var EntityManagerInterface */ private $em; + /** + * @var ClassMetadata|null + */ + private $foundMetadata; + /** * Constructor. * - * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $classMetadata - * @param \Doctrine\ORM\EntityManager $em + * @param string $className + * @param EntityManagerInterface $em */ - public function __construct($className, EntityManager $em) + public function __construct($className, EntityManagerInterface $em) { - $this->className = $className; + $this->className = (string) $className; $this->em = $em; } + /** + * @param ClassMetadata|null $classMetadata + */ + public function setFoundMetadata(ClassMetadata $classMetadata = null) + { + $this->foundMetadata = $classMetadata; + } + + /** + * @return ClassMetadata|null + */ + public function getFoundMetadata() + { + return $this->foundMetadata; + } + /** * Retrieve associated ClassMetadata. * From d91b0b4938dd8d0040d29475982394a3bc47c9d6 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 11 Nov 2014 15:51:24 +0100 Subject: [PATCH 08/23] Minor CS fixes in the `ResolveTargetEntityListener` --- .../ORM/Tools/ResolveTargetEntityListener.php | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php b/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php index a0f1e0b39..1f0c67594 100644 --- a/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php +++ b/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php @@ -37,10 +37,13 @@ use Doctrine\ORM\Events; class ResolveTargetEntityListener implements EventSubscriber { /** - * @var array + * @var array[] indexed by original entity name */ private $resolveTargetEntities = array(); + /** + * {@inheritDoc} + */ public function getSubscribedEvents() { return array( @@ -48,6 +51,7 @@ class ResolveTargetEntityListener implements EventSubscriber Events::onClassMetadataNotFound ); } + /** * Adds a target-entity class name to resolve to a new class name. * @@ -59,14 +63,25 @@ class ResolveTargetEntityListener implements EventSubscriber */ public function addResolveTargetEntity($originalEntity, $newEntity, array $mapping) { - $mapping['targetEntity'] = ltrim($newEntity, "\\"); + $mapping['targetEntity'] = ltrim($newEntity, "\\"); $this->resolveTargetEntities[ltrim($originalEntity, "\\")] = $mapping; } + /** + * @param OnClassMetadataNotFoundEventArgs $args + * + * @internal this is an event callback, and should not be called directly + * + * @return void + */ public function onClassMetadataNotFound(OnClassMetadataNotFoundEventArgs $args) { if (array_key_exists($args->getClassName(), $this->resolveTargetEntities)) { - $args->getEntityManager()->getClassMetadata($this->resolveTargetEntities[$args->getClassname()]['targetEntity']); + $args->setFoundMetadata( + $args + ->getEntityManager() + ->getClassMetadata($this->resolveTargetEntities[$args->getClassname()]['targetEntity']) + ); } } @@ -76,6 +91,8 @@ class ResolveTargetEntityListener implements EventSubscriber * @param LoadClassMetadataEventArgs $args * * @return void + * + * @internal this is an event callback, and should not be called directly */ public function loadClassMetadata(LoadClassMetadataEventArgs $args) { From e4cbdb57adb80d1f00d3a2fcaaa05f8e3b1628b8 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 11 Nov 2014 15:51:52 +0100 Subject: [PATCH 09/23] Enabling fallback logic in metadata loading --- .../ORM/Mapping/ClassMetadataFactory.php | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 79e2fc8de..498ff5400 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -22,6 +22,10 @@ namespace Doctrine\ORM\Mapping; use Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory; use Doctrine\Common\Persistence\Mapping\ClassMetadata as ClassMetadataInterface; use Doctrine\Common\Persistence\Mapping\ReflectionService; +use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs; +use ReflectionException; +use Doctrine\ORM\ORMException; +use Doctrine\ORM\EntityManager; use Doctrine\DBAL\Platforms; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; @@ -78,7 +82,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory } /** - * {@inheritDoc}. + * {@inheritDoc} */ protected function initialize() { @@ -88,6 +92,23 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory $this->initialized = true; } + /** + * {@inheritDoc} + */ + protected function onNotFoundMetadata($className) + { + if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) { + return; + } + + $eventArgs = new OnClassMetadataNotFoundEventArgs($className, $this->em); + + $this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs); + + // @todo to be discussed: event is used as a mutable data vector here, which may be undesired. + return $eventArgs->getFoundMetadata(); + } + /** * {@inheritDoc} */ From ce4df761dfc6412f0e7666307573af5f042a4354 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 11 Nov 2014 15:52:51 +0100 Subject: [PATCH 10/23] Explicitly requiring `doctrine/common` with at least version `2.5` --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 3b90dff5c..06748249f 100644 --- a/composer.json +++ b/composer.json @@ -18,6 +18,7 @@ "doctrine/collections": "~1.2", "doctrine/dbal": ">=2.5-dev,<2.6-dev", "doctrine/instantiator": "~1.0.1", + "doctrine/common": ">=2.5-dev,<2.6-dev", "symfony/console": "~2.5" }, "require-dev": { From a710706c81f7f7de32c51912130272b86f7be4fc Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 13 Jan 2015 20:00:40 +0100 Subject: [PATCH 11/23] #385 #1181 DDC-3385 - optimized imports, removing duplicate imports (caused by rebase conflicts) --- lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 498ff5400..5a9a97b83 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -22,13 +22,10 @@ namespace Doctrine\ORM\Mapping; use Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory; use Doctrine\Common\Persistence\Mapping\ClassMetadata as ClassMetadataInterface; use Doctrine\Common\Persistence\Mapping\ReflectionService; -use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs; -use ReflectionException; -use Doctrine\ORM\ORMException; -use Doctrine\ORM\EntityManager; use Doctrine\DBAL\Platforms; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; +use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs; use Doctrine\ORM\Events; use Doctrine\ORM\Id\BigIntegerIdentityGenerator; use Doctrine\ORM\Id\IdentityGenerator; From 8ea394e7780ee4090474ddaeb6439e76c787b7ad Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 13 Jan 2015 20:33:10 +0100 Subject: [PATCH 12/23] #385 #1181 DDC-3385 - removing `@todo` that was discussed/cleared --- lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 5a9a97b83..dd65c3932 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -102,7 +102,6 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory $this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs); - // @todo to be discussed: event is used as a mutable data vector here, which may be undesired. return $eventArgs->getFoundMetadata(); } From eaa9187dd8312fcad7d7f5eb7f617d10fc319ad2 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 13 Jan 2015 20:33:30 +0100 Subject: [PATCH 13/23] #385 #1181 DDC-3385 - minor CS fix (spacing) --- lib/Doctrine/ORM/Events.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Doctrine/ORM/Events.php b/lib/Doctrine/ORM/Events.php index d089a4366..36b39623c 100644 --- a/lib/Doctrine/ORM/Events.php +++ b/lib/Doctrine/ORM/Events.php @@ -119,6 +119,7 @@ final class Events * @var string */ const loadClassMetadata = 'loadClassMetadata'; + /** * The preLoadClassMetadata event occurs before the mapping metadata for a * class is loaded from a mapping source (annotations/xml/yaml). From 94a2036d5041fd2ee466237fe47364a321c9d552 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 13 Jan 2015 20:37:28 +0100 Subject: [PATCH 14/23] #385 #1181 DDC-3385 - adding `@group` annotation to newly introduced tests --- .../Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php index 8ea01fce2..699f936f1 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php @@ -324,6 +324,11 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals('group-id', $groups['joinTable']['inverseJoinColumns'][0]['referencedColumnName']); } + /** + * @group DDC-3385 + * @group 1181 + * @group 385 + */ public function testFallbackLoadingCausesEventTriggeringThatCanModifyFetchedMetadata() { $test = $this; From f14063def574614deebd2bca0b586c1832b29cbd Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 13 Jan 2015 20:38:01 +0100 Subject: [PATCH 15/23] #385 #1181 DDC-3385 - splitting test case to verify interface-first fetching of metadata (via fallback logic) --- .../Tools/ResolveTargetEntityListenerTest.php | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php b/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php index cbc6d40a7..fdee6e8c2 100644 --- a/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/ResolveTargetEntityListenerTest.php @@ -51,10 +51,9 @@ class ResolveTargetEntityListenerTest extends \Doctrine\Tests\OrmTestCase ); $evm->addEventSubscriber($this->listener); - $this->assertNotNull($this->factory->getMetadataFor('Doctrine\Tests\ORM\Tools\ResolveTargetInterface')); - - $cm = $this->factory->getMetadataFor('Doctrine\Tests\ORM\Tools\ResolveTargetEntity'); + $cm = $this->factory->getMetadataFor('Doctrine\Tests\ORM\Tools\ResolveTargetEntity'); $meta = $cm->associationMappings; + $this->assertSame('Doctrine\Tests\ORM\Tools\TargetEntity', $meta['manyToMany']['targetEntity']); $this->assertSame('Doctrine\Tests\ORM\Tools\ResolveTargetEntity', $meta['manyToOne']['targetEntity']); $this->assertSame('Doctrine\Tests\ORM\Tools\ResolveTargetEntity', $meta['oneToMany']['targetEntity']); @@ -63,6 +62,26 @@ class ResolveTargetEntityListenerTest extends \Doctrine\Tests\OrmTestCase $this->assertSame($cm, $this->factory->getMetadataFor('Doctrine\Tests\ORM\Tools\ResolveTargetInterface')); } + /** + * @group DDC-3385 + * @group 1181 + * @group 385 + */ + public function testResolveTargetEntityListenerCanRetrieveTargetEntityByInterfaceName() + { + $this->listener->addResolveTargetEntity( + 'Doctrine\Tests\ORM\Tools\ResolveTargetInterface', + 'Doctrine\Tests\ORM\Tools\ResolveTargetEntity', + array() + ); + + $this->em->getEventManager()->addEventSubscriber($this->listener); + + $cm = $this->factory->getMetadataFor('Doctrine\Tests\ORM\Tools\ResolveTargetInterface'); + + $this->assertSame($this->factory->getMetadataFor('Doctrine\Tests\ORM\Tools\ResolveTargetEntity'), $cm); + } + /** * @group DDC-2109 */ From 06f256b4103cd2e3013c59bab9e6687588c6afdd Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 13 Jan 2015 20:45:00 +0100 Subject: [PATCH 16/23] #385 #1181 DDC-3385 - documenting `onClassMetadataNotFound` event --- docs/en/reference/events.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/en/reference/events.rst b/docs/en/reference/events.rst index 65acd2466..6ec2f092d 100644 --- a/docs/en/reference/events.rst +++ b/docs/en/reference/events.rst @@ -173,6 +173,10 @@ the life-time of their registered entities. - loadClassMetadata - The loadClassMetadata event occurs after the mapping metadata for a class has been loaded from a mapping source (annotations/xml/yaml). This event is not a lifecycle callback. +- onClassMetadataNotFound - Loading class metadata for a particular + requested class name failed. Manipulating the given event args instance + allows providing fallback metadata even when no actual metadata exists + or could be found. This event is not a lifecycle callback. - preFlush - The preFlush event occurs at the very beginning of a flush operation. This event is not a lifecycle callback. - onFlush - The onFlush event occurs after the change-sets of all From 6021e3a1b8d6b8316edc870c000e56906cae510f Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 13 Jan 2015 20:49:08 +0100 Subject: [PATCH 17/23] #385 #1181 DDC-3385 - Updating docblock of `OnClassMetadataNotFoundEventArgs` --- .../ORM/Event/OnClassMetadataNotFoundEventArgs.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php b/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php index 6d7b128ed..f71e0336a 100644 --- a/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php +++ b/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php @@ -25,10 +25,13 @@ use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManagerInterface; /** - * Class that holds event arguments for a loadMetadata event. + * Class that holds event arguments for a `onClassMetadataNotFound` event. * - * @author Jonathan H. Wage - * @since 2.3 + * This object is mutable by design, allowing callbacks having access to it to set the + * found metadata in it, and therefore "cancelling" a `onClassMetadataNotFound` event + * + * @author Marco Pivetta + * @since 2.5 */ class OnClassMetadataNotFoundEventArgs extends EventArgs { From 762e798d22cecc9be8c973f121a0e8798f029682 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 13 Jan 2015 20:56:21 +0100 Subject: [PATCH 18/23] #385 #1181 DDC-3385 - extending `OnClassMetadataNotFoundEventArgs` from `ManagerEventArgs` instead of generic `EventArgs` --- .../OnClassMetadataNotFoundEventArgs.php | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php b/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php index f71e0336a..4fb4c8f94 100644 --- a/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php +++ b/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php @@ -20,7 +20,9 @@ namespace Doctrine\ORM\Event; use Doctrine\Common\EventArgs; +use Doctrine\Common\Persistence\Event\ManagerEventArgs; use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\ObjectManager; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManagerInterface; @@ -33,18 +35,13 @@ use Doctrine\ORM\EntityManagerInterface; * @author Marco Pivetta * @since 2.5 */ -class OnClassMetadataNotFoundEventArgs extends EventArgs +class OnClassMetadataNotFoundEventArgs extends ManagerEventArgs { /** * @var string */ private $className; - /** - * @var EntityManagerInterface - */ - private $em; - /** * @var ClassMetadata|null */ @@ -53,13 +50,14 @@ class OnClassMetadataNotFoundEventArgs extends EventArgs /** * Constructor. * - * @param string $className - * @param EntityManagerInterface $em + * @param string $className + * @param ObjectManager $objectManager */ - public function __construct($className, EntityManagerInterface $em) + public function __construct($className, ObjectManager $objectManager) { $this->className = (string) $className; - $this->em = $em; + + parent::__construct($objectManager); } /** @@ -81,21 +79,11 @@ class OnClassMetadataNotFoundEventArgs extends EventArgs /** * Retrieve associated ClassMetadata. * - * @return \Doctrine\ORM\Mapping\ClassMetadataInfo + * @return string */ public function getClassName() { return $this->className; } - - /** - * Retrieve associated EntityManager. - * - * @return \Doctrine\ORM\EntityManager - */ - public function getEntityManager() - { - return $this->em; - } } From 6debf2c9097af22c55767eae889b7952881645db Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 13 Jan 2015 20:56:40 +0100 Subject: [PATCH 19/23] #385 #1181 DDC-3385 - aligning tests to new `OnClassMetadataNotFoundEventArgs` API --- tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php index 699f936f1..54d2c9eac 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php @@ -348,7 +348,7 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase ->will($this->returnCallback(function (OnClassMetadataNotFoundEventArgs $args) use ($metadata, $em, $test) { $test->assertNull($args->getFoundMetadata()); $test->assertSame('Foo', $args->getClassName()); - $test->assertSame($em, $args->getEntityManager()); + $test->assertSame($em, $args->getObjectManager()); $args->setFoundMetadata($metadata); })); From 662b49608cfda0b78bfa2b965d576e84742a5452 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 13 Jan 2015 20:56:57 +0100 Subject: [PATCH 20/23] #385 #1181 DDC-3385 - aligning tests and implementation to new `OnClassMetadataNotFoundEventArgs` API --- lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php b/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php index 1f0c67594..574c9c28e 100644 --- a/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php +++ b/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php @@ -79,7 +79,7 @@ class ResolveTargetEntityListener implements EventSubscriber if (array_key_exists($args->getClassName(), $this->resolveTargetEntities)) { $args->setFoundMetadata( $args - ->getEntityManager() + ->getObjectManager() ->getClassMetadata($this->resolveTargetEntities[$args->getClassname()]['targetEntity']) ); } @@ -96,6 +96,7 @@ class ResolveTargetEntityListener implements EventSubscriber */ public function loadClassMetadata(LoadClassMetadataEventArgs $args) { + /* @var $cm \Doctrine\ORM\Mapping\ClassMetadata */ $cm = $args->getClassMetadata(); foreach ($cm->associationMappings as $mapping) { From d712ad97a5b36954224111095946ee7818ab1c7b Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 13 Jan 2015 21:04:50 +0100 Subject: [PATCH 21/23] #385 #1181 DDC-3385 - test coverage for `OnClassMetadataNotFoundEventArgs` impl --- .../OnClassMetadataNotFoundEventArgsTest.php | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/Doctrine/Tests/ORM/Event/OnClassMetadataNotFoundEventArgsTest.php diff --git a/tests/Doctrine/Tests/ORM/Event/OnClassMetadataNotFoundEventArgsTest.php b/tests/Doctrine/Tests/ORM/Event/OnClassMetadataNotFoundEventArgsTest.php new file mode 100644 index 000000000..3aad1c04c --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Event/OnClassMetadataNotFoundEventArgsTest.php @@ -0,0 +1,38 @@ +getMock('Doctrine\Common\Persistence\ObjectManager'); + + $args = new OnClassMetadataNotFoundEventArgs('foo', $objectManager); + + $this->assertSame('foo', $args->getClassName()); + $this->assertSame($objectManager, $args->getObjectManager()); + + $this->assertNull($args->getFoundMetadata()); + + /* @var $metadata \Doctrine\Common\Persistence\Mapping\ClassMetadata */ + $metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + + $args->setFoundMetadata($metadata); + + $this->assertSame($metadata, $args->getFoundMetadata()); + + $args->setFoundMetadata(null); + + $this->assertNull($args->getFoundMetadata()); + } +} From ae8ab0812eddb5dec7623716dbfae0fda1286eac Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 13 Jan 2015 21:14:32 +0100 Subject: [PATCH 22/23] #385 #1181 DDC-3385 - fixing `OnClassMetadataNotFoundEventArgs` docblocks as per @deeky666's review --- lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php b/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php index 4fb4c8f94..155b1baf6 100644 --- a/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php +++ b/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php @@ -77,7 +77,7 @@ class OnClassMetadataNotFoundEventArgs extends ManagerEventArgs } /** - * Retrieve associated ClassMetadata. + * Retrieve class name for which a failed metadata fetch attempt was executed * * @return string */ From ba5378feccb49b5c8bb9fc3d2988b78e6fc6644e Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 13 Jan 2015 21:15:34 +0100 Subject: [PATCH 23/23] #385 #1181 DDC-3385 - fixing `Events` docblocks as per @deeky666's review --- lib/Doctrine/ORM/Events.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Events.php b/lib/Doctrine/ORM/Events.php index 36b39623c..e16b47a42 100644 --- a/lib/Doctrine/ORM/Events.php +++ b/lib/Doctrine/ORM/Events.php @@ -121,8 +121,8 @@ final class Events const loadClassMetadata = 'loadClassMetadata'; /** - * The preLoadClassMetadata event occurs before the mapping metadata for a - * class is loaded from a mapping source (annotations/xml/yaml). + * The onClassMetadataNotFound event occurs whenever loading metadata for a class + * failed. * * @var string */