1
0
mirror of synced 2025-01-19 15:01:40 +03:00

Merge pull request #1181 from Ocramius/feature/#385-support-fetching-entities-by-aliased-name

Support fetching entities by aliased name
This commit is contained in:
Steve Müller 2015-01-14 13:54:33 +01:00
commit 00a8265fb9
9 changed files with 267 additions and 10 deletions

View File

@ -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": {

View File

@ -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

View File

@ -0,0 +1,89 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\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;
/**
* Class that holds event arguments for a `onClassMetadataNotFound` event.
*
* 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 <ocramius@gmail.com>
* @since 2.5
*/
class OnClassMetadataNotFoundEventArgs extends ManagerEventArgs
{
/**
* @var string
*/
private $className;
/**
* @var ClassMetadata|null
*/
private $foundMetadata;
/**
* Constructor.
*
* @param string $className
* @param ObjectManager $objectManager
*/
public function __construct($className, ObjectManager $objectManager)
{
$this->className = (string) $className;
parent::__construct($objectManager);
}
/**
* @param ClassMetadata|null $classMetadata
*/
public function setFoundMetadata(ClassMetadata $classMetadata = null)
{
$this->foundMetadata = $classMetadata;
}
/**
* @return ClassMetadata|null
*/
public function getFoundMetadata()
{
return $this->foundMetadata;
}
/**
* Retrieve class name for which a failed metadata fetch attempt was executed
*
* @return string
*/
public function getClassName()
{
return $this->className;
}
}

View File

@ -120,6 +120,14 @@ final class Events
*/
const loadClassMetadata = 'loadClassMetadata';
/**
* The onClassMetadataNotFound event occurs whenever loading metadata for a class
* failed.
*
* @var string
*/
const onClassMetadataNotFound = 'onClassMetadataNotFound';
/**
* The preFlush event occurs when the EntityManager#flush() operation is invoked,
* but before any changes to managed entities have been calculated. This event is

View File

@ -25,6 +25,7 @@ use Doctrine\Common\Persistence\Mapping\ReflectionService;
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;
@ -78,7 +79,7 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
}
/**
* {@inheritDoc}.
* {@inheritDoc}
*/
protected function initialize()
{
@ -88,6 +89,22 @@ 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);
return $eventArgs->getFoundMetadata();
}
/**
* {@inheritDoc}
*/

View File

@ -20,7 +20,10 @@
namespace Doctrine\ORM\Tools;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Events;
/**
* ResolveTargetEntityListener
@ -31,13 +34,24 @@ use Doctrine\ORM\Mapping\ClassMetadata;
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @since 2.2
*/
class ResolveTargetEntityListener
class ResolveTargetEntityListener implements EventSubscriber
{
/**
* @var array
* @var array[] indexed by original entity name
*/
private $resolveTargetEntities = array();
/**
* {@inheritDoc}
*/
public function getSubscribedEvents()
{
return array(
Events::loadClassMetadata,
Events::onClassMetadataNotFound
);
}
/**
* Adds a target-entity class name to resolve to a new class name.
*
@ -49,19 +63,40 @@ class ResolveTargetEntityListener
*/
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->setFoundMetadata(
$args
->getObjectManager()
->getClassMetadata($this->resolveTargetEntities[$args->getClassname()]['targetEntity'])
);
}
}
/**
* Processes event and resolves new target entity names.
*
* @param LoadClassMetadataEventArgs $args
*
* @return void
*
* @internal this is an event callback, and should not be called directly
*/
public function loadClassMetadata(LoadClassMetadataEventArgs $args)
{
/* @var $cm \Doctrine\ORM\Mapping\ClassMetadata */
$cm = $args->getClassMetadata();
foreach ($cm->associationMappings as $mapping) {
@ -69,6 +104,12 @@ class ResolveTargetEntityListener
$this->remapAssociation($cm, $mapping);
}
}
foreach ($this->resolveTargetEntities as $interface => $data) {
if ($data['targetEntity'] == $cm->getName()) {
$args->getEntityManager()->getMetadataFactory()->setMetadataFor($interface, $cm);
}
}
}
/**

View File

@ -0,0 +1,38 @@
<?php
namespace Doctrine\Tests\ORM;
use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs;
use PHPUnit_Framework_TestCase;
/**
* Tests for {@see \Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs}
*
* @covers \Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs
*/
class OnClassMetadataNotFoundEventArgsTest extends PHPUnit_Framework_TestCase
{
public function testEventArgsMutability()
{
/* @var $objectManager \Doctrine\Common\Persistence\ObjectManager */
$objectManager = $this->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());
}
}

View File

@ -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,40 @@ 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;
/* @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->getObjectManager());
$args->setFoundMetadata($metadata);
}));
$eventManager->addEventListener(array(Events::onClassMetadataNotFound), $listener);
$this->assertSame($metadata, $cmf->getMetadataFor('Foo'));
}
/**
* @group DDC-3427
*/

View File

@ -2,9 +2,9 @@
namespace Doctrine\Tests\ORM\Tools;
use Doctrine\ORM\Events;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\Tools\ResolveTargetEntityListener;
use Doctrine\ORM\Events;
class ResolveTargetEntityListenerTest extends \Doctrine\Tests\OrmTestCase
{
@ -29,9 +29,8 @@ 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->listener = new ResolveTargetEntityListener;
$this->factory = $this->em->getMetadataFactory();
$this->listener = new ResolveTargetEntityListener();
}
/**
@ -50,13 +49,37 @@ class ResolveTargetEntityListenerTest extends \Doctrine\Tests\OrmTestCase
'Doctrine\Tests\ORM\Tools\TargetEntity',
array()
);
$evm->addEventListener(Events::loadClassMetadata, $this->listener);
$cm = $this->factory->getMetadataFor('Doctrine\Tests\ORM\Tools\ResolveTargetEntity');
$evm->addEventSubscriber($this->listener);
$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']);
$this->assertSame('Doctrine\Tests\ORM\Tools\TargetEntity', $meta['oneToOne']['targetEntity']);
$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);
}
/**