diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index 07af294b1..333107c73 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -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: diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 63ac01c25..befc2973c 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -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]; diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index d2638bea5..48508981d 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -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 { diff --git a/lib/Doctrine/ORM/Proxy/ProxyFactory.php b/lib/Doctrine/ORM/Proxy/ProxyFactory.php index 21d033e7e..df10798e1 100644 --- a/lib/Doctrine/ORM/Proxy/ProxyFactory.php +++ b/lib/Doctrine/ORM/Proxy/ProxyFactory.php @@ -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); diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 410f3b2c6..c1244934b 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -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) { diff --git a/tests/Doctrine/Tests/ORM/Functional/ProxiesLikeEntitiesTest.php b/tests/Doctrine/Tests/ORM/Functional/ProxiesLikeEntitiesTest.php new file mode 100644 index 000000000..52ac8f591 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/ProxiesLikeEntitiesTest.php @@ -0,0 +1,133 @@ +_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(); + } +} \ No newline at end of file