Merge pull request #315 from Ocramius/getclass-on-proxies-refactoring
Allowing proxies to be passed to ORM public API
This commit is contained in:
commit
e09a9c7deb
@ -22,7 +22,7 @@ namespace Doctrine\ORM;
|
||||
use Doctrine\DBAL\Types\Type,
|
||||
Doctrine\DBAL\Cache\QueryCacheProfile,
|
||||
Doctrine\ORM\Query\QueryException,
|
||||
Doctrine\ORM\Internal\Hydration\CacheHydrator;
|
||||
Doctrine\Common\Util\ClassUtils;
|
||||
|
||||
/**
|
||||
* Base contract for ORM queries. Base class for Query and NativeQuery.
|
||||
@ -241,7 +241,7 @@ abstract class AbstractQuery
|
||||
|
||||
return $value;
|
||||
|
||||
case is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value)):
|
||||
case is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value)):
|
||||
return $this->convertObjectParameterToScalarValue($value);
|
||||
|
||||
default:
|
||||
|
@ -24,6 +24,7 @@ use ReflectionException,
|
||||
Doctrine\ORM\EntityManager,
|
||||
Doctrine\DBAL\Platforms,
|
||||
Doctrine\ORM\Events,
|
||||
Doctrine\Common\Util\ClassUtils,
|
||||
Doctrine\Common\Persistence\Mapping\RuntimeReflectionService,
|
||||
Doctrine\Common\Persistence\Mapping\ReflectionService,
|
||||
Doctrine\Common\Persistence\Mapping\ClassMetadataFactory as ClassMetadataFactoryInterface;
|
||||
@ -154,41 +155,44 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
|
||||
*/
|
||||
public function getMetadataFor($className)
|
||||
{
|
||||
if ( ! isset($this->loadedMetadata[$className])) {
|
||||
$realClassName = $className;
|
||||
if (isset($this->loadedMetadata[$className])) {
|
||||
return $this->loadedMetadata[$className];
|
||||
}
|
||||
|
||||
// Check for namespace alias
|
||||
if (strpos($className, ':') !== false) {
|
||||
list($namespaceAlias, $simpleClassName) = explode(':', $className);
|
||||
$realClassName = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
|
||||
$realClassName = $className;
|
||||
|
||||
if (isset($this->loadedMetadata[$realClassName])) {
|
||||
// We do not have the alias name in the map, include it
|
||||
$this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
|
||||
// Check for namespace alias
|
||||
if (strpos($className, ':') !== false) {
|
||||
list($namespaceAlias, $simpleClassName) = explode(':', $className);
|
||||
$realClassName = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
|
||||
} else {
|
||||
$realClassName = ClassUtils::getRealClass($realClassName);
|
||||
}
|
||||
|
||||
return $this->loadedMetadata[$realClassName];
|
||||
}
|
||||
}
|
||||
if (isset($this->loadedMetadata[$realClassName])) {
|
||||
// We do not have the alias name in the map, include it
|
||||
$this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
|
||||
return $this->loadedMetadata[$realClassName];
|
||||
}
|
||||
|
||||
if ($this->cacheDriver) {
|
||||
if (($cached = $this->cacheDriver->fetch("$realClassName\$CLASSMETADATA")) !== false) {
|
||||
$this->wakeupReflection($cached, $this->getReflectionService());
|
||||
$this->loadedMetadata[$realClassName] = $cached;
|
||||
} else {
|
||||
foreach ($this->loadMetadata($realClassName) as $loadedClassName) {
|
||||
$this->cacheDriver->save(
|
||||
"$loadedClassName\$CLASSMETADATA", $this->loadedMetadata[$loadedClassName], null
|
||||
);
|
||||
}
|
||||
}
|
||||
if ($this->cacheDriver) {
|
||||
if (($cached = $this->cacheDriver->fetch("$realClassName\$CLASSMETADATA")) !== false) {
|
||||
$this->wakeupReflection($cached, $this->getReflectionService());
|
||||
$this->loadedMetadata[$realClassName] = $cached;
|
||||
} else {
|
||||
$this->loadMetadata($realClassName);
|
||||
foreach ($this->loadMetadata($realClassName) as $loadedClassName) {
|
||||
$this->cacheDriver->save(
|
||||
"$loadedClassName\$CLASSMETADATA", $this->loadedMetadata[$loadedClassName], null
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->loadMetadata($realClassName);
|
||||
}
|
||||
|
||||
if ($className != $realClassName) {
|
||||
// We do not have the alias name in the map, include it
|
||||
$this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
|
||||
}
|
||||
if ($className != $realClassName) {
|
||||
// We do not have the alias name in the map, include it
|
||||
$this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
|
||||
}
|
||||
|
||||
return $this->loadedMetadata[$className];
|
||||
|
@ -32,7 +32,8 @@ use PDO,
|
||||
Doctrine\ORM\Mapping\MappingException,
|
||||
Doctrine\ORM\Mapping\ClassMetadata,
|
||||
Doctrine\ORM\Events,
|
||||
Doctrine\ORM\Event\LifecycleEventArgs;
|
||||
Doctrine\ORM\Event\LifecycleEventArgs,
|
||||
Doctrine\Common\Util\ClassUtils;
|
||||
|
||||
/**
|
||||
* A BasicEntityPersiter maps an entity to a single table in a relational database.
|
||||
@ -1499,7 +1500,7 @@ class BasicEntityPersister
|
||||
*/
|
||||
private function getIndividualValue($value)
|
||||
{
|
||||
if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) {
|
||||
if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) {
|
||||
if ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED) {
|
||||
$idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
|
||||
} else {
|
||||
|
@ -93,10 +93,6 @@ class ProxyFactory
|
||||
require $fileName;
|
||||
}
|
||||
|
||||
if ( ! $this->_em->getMetadataFactory()->hasMetadataFor($fqn)) {
|
||||
$this->_em->getMetadataFactory()->setMetadataFor($fqn, $this->_em->getClassMetadata($className));
|
||||
}
|
||||
|
||||
$entityPersister = $this->_em->getUnitOfWork()->getEntityPersister($className);
|
||||
|
||||
return new $fqn($entityPersister, $identifier);
|
||||
|
@ -886,7 +886,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$hasListeners = $this->evm->hasListeners(Events::postPersist);
|
||||
|
||||
foreach ($this->entityInsertions as $oid => $entity) {
|
||||
if (get_class($entity) !== $className) {
|
||||
if ($this->em->getClassMetadata(get_class($entity))->name !== $className) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -945,7 +945,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$hasPostUpdateListeners = $this->evm->hasListeners(Events::postUpdate);
|
||||
|
||||
foreach ($this->entityUpdates as $oid => $entity) {
|
||||
if ( ! (get_class($entity) === $className || $entity instanceof Proxy && get_parent_class($entity) === $className)) {
|
||||
if ($this->em->getClassMetadata(get_class($entity))->name !== $className) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -992,7 +992,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$hasListeners = $this->evm->hasListeners(Events::postRemove);
|
||||
|
||||
foreach ($this->entityDeletions as $oid => $entity) {
|
||||
if ( ! (get_class($entity) == $className || $entity instanceof Proxy && get_parent_class($entity) == $className)) {
|
||||
if ($this->em->getClassMetadata(get_class($entity))->name !== $className) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1043,7 +1043,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$newNodes = array();
|
||||
|
||||
foreach ($entityChangeSet as $entity) {
|
||||
$className = get_class($entity);
|
||||
$className = $this->em->getClassMetadata(get_class($entity))->name;
|
||||
|
||||
if ($calc->hasClass($className)) {
|
||||
continue;
|
||||
@ -1357,7 +1357,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
}
|
||||
|
||||
// db lookup
|
||||
if ($this->getEntityPersister(get_class($entity))->exists($entity)) {
|
||||
if ($this->getEntityPersister($class->name)->exists($entity)) {
|
||||
return self::STATE_DETACHED;
|
||||
}
|
||||
|
||||
@ -1374,7 +1374,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
}
|
||||
|
||||
// db lookup
|
||||
if ($this->getEntityPersister(get_class($entity))->exists($entity)) {
|
||||
if ($this->getEntityPersister($class->name)->exists($entity)) {
|
||||
return self::STATE_DETACHED;
|
||||
}
|
||||
|
||||
@ -2151,13 +2151,12 @@ class UnitOfWork implements PropertyChangedListener
|
||||
throw ORMInvalidArgumentException::entityNotManaged($entity);
|
||||
}
|
||||
|
||||
$entityName = get_class($entity);
|
||||
$class = $this->em->getClassMetadata($entityName);
|
||||
$class = $this->em->getClassMetadata(get_class($entity));
|
||||
|
||||
switch ($lockMode) {
|
||||
case \Doctrine\DBAL\LockMode::OPTIMISTIC;
|
||||
if ( ! $class->isVersioned) {
|
||||
throw OptimisticLockException::notVersioned($entityName);
|
||||
throw OptimisticLockException::notVersioned($class->name);
|
||||
}
|
||||
|
||||
if ($lockVersion === null) {
|
||||
|
133
tests/Doctrine/Tests/ORM/Functional/ProxiesLikeEntitiesTest.php
Normal file
133
tests/Doctrine/Tests/ORM/Functional/ProxiesLikeEntitiesTest.php
Normal file
@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\Common\Util\ClassUtils,
|
||||
Doctrine\Tests\Models\CMS\CmsUser,
|
||||
Doctrine\Tests\Proxies\__CG__\Doctrine\Tests\Models\CMS\CmsUser as Proxy;
|
||||
|
||||
/**
|
||||
* Test that Doctrine ORM correctly works with proxy instances exactly like with ordinary Entities
|
||||
*
|
||||
* The test considers two possible cases:
|
||||
* a) __initialized__ = true and no identifier set in proxy
|
||||
* b) __initialized__ = false and identifier set in proxy and in property
|
||||
* @todo All other cases would cause lazy loading issues
|
||||
*/
|
||||
class ProxiesLikeEntitiesTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
/**
|
||||
* @var CmsUser
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
try {
|
||||
$this->_schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'),
|
||||
));
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
$this->user = new CmsUser();
|
||||
$this->user->username = 'ocramius';
|
||||
$this->user->name = 'Marco';
|
||||
$this->_em->persist($this->user);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that a proxy can be successfully persisted and updated
|
||||
*/
|
||||
public function testPersistUpdate()
|
||||
{
|
||||
// Considering case (a)
|
||||
$persister = $this->_em->getUnitOfWork()->getEntityPersister('Doctrine\Tests\Models\CMS\CmsUser');
|
||||
$proxy = new Proxy($persister, array());
|
||||
$proxy->__isInitialized__ = true;
|
||||
$proxy->username = 'ocra';
|
||||
$proxy->name = 'Marco';
|
||||
$this->_em->persist($proxy);
|
||||
$this->_em->flush();
|
||||
$this->assertNotNull($proxy->getId());
|
||||
$proxy->name = 'Marco Pivetta';
|
||||
$this
|
||||
->_em
|
||||
->getUnitOfWork()
|
||||
->computeChangeSet($this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'), $proxy);
|
||||
$this->assertNotEmpty($this->_em->getUnitOfWork()->getEntityChangeSet($proxy));
|
||||
$this->assertEquals('Marco Pivetta', $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $proxy->getId())->name);
|
||||
$this->_em->remove($proxy);
|
||||
$this->_em->flush();
|
||||
}
|
||||
|
||||
public function testEntityWithIdentifier()
|
||||
{
|
||||
// Considering case (b)
|
||||
$persister = $this->_em->getUnitOfWork()->getEntityPersister('Doctrine\Tests\Models\CMS\CmsUser');
|
||||
$uninitializedProxy = new Proxy($persister, array('id' => $this->user->getId()));
|
||||
$uninitializedProxy->id = $this->user->getId();
|
||||
$uninitializedProxy->username = 'ocra';
|
||||
$uninitializedProxy->name = 'Marco Pivetta';
|
||||
$this->_em->persist($uninitializedProxy);
|
||||
$this->_em->flush();
|
||||
$this->assertEquals($this->user->getId(), $uninitializedProxy->getId());
|
||||
$this->_em->remove($uninitializedProxy);
|
||||
$this->_em->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifying that proxies can be used without problems as query parameters
|
||||
*/
|
||||
public function testProxyAsDqlParameterPersist()
|
||||
{
|
||||
$persister = $this->_em->getUnitOfWork()->getEntityPersister('Doctrine\Tests\Models\CMS\CmsUser');
|
||||
$proxy = new Proxy($persister, array('id' => $this->user->getId()));
|
||||
$proxy->id = $this->user->getId();
|
||||
$result = $this
|
||||
->_em
|
||||
->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u = ?1')
|
||||
->setParameter(1, $proxy)
|
||||
->getSingleResult();
|
||||
$this->assertSame($this->user->getId(), $result->getId());
|
||||
$this->_em->remove($proxy);
|
||||
$this->_em->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifying that proxies can be used without problems as query parameters
|
||||
*/
|
||||
public function testFindWithProxyName()
|
||||
{
|
||||
$result = $this
|
||||
->_em
|
||||
->find('Doctrine\Tests\Proxies\__CG__\Doctrine\Tests\Models\CMS\CmsUser', $this->user->getId());
|
||||
$this->assertSame($this->user->getId(), $result->getId());
|
||||
$this->_em->clear();
|
||||
$result = $this
|
||||
->_em
|
||||
->getReference('Doctrine\Tests\Proxies\__CG__\Doctrine\Tests\Models\CMS\CmsUser', $this->user->getId());
|
||||
$this->assertSame($this->user->getId(), $result->getId());
|
||||
$this->_em->clear();
|
||||
$result = $this
|
||||
->_em
|
||||
->getRepository('Doctrine\Tests\Proxies\__CG__\Doctrine\Tests\Models\CMS\CmsUser')
|
||||
->findOneBy(array('username' => $this->user->username));
|
||||
$this->assertSame($this->user->getId(), $result->getId());
|
||||
$this->_em->clear();
|
||||
$result = $this
|
||||
->_em
|
||||
->createQuery('SELECT u FROM Doctrine\Tests\Proxies\__CG__\Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1')
|
||||
->setParameter(1, $this->user->getId())
|
||||
->getSingleResult();
|
||||
$this->assertSame($this->user->getId(), $result->getId());
|
||||
$this->_em->clear();
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->_em->createQuery('DELETE FROM Doctrine\Tests\Models\CMS\CmsUser u')->execute();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user