diff --git a/lib/Doctrine/ORM/Configuration.php b/lib/Doctrine/ORM/Configuration.php index 371abd7e5..b54e7d5d4 100644 --- a/lib/Doctrine/ORM/Configuration.php +++ b/lib/Doctrine/ORM/Configuration.php @@ -45,7 +45,6 @@ class Configuration extends \Doctrine\DBAL\Configuration 'metadataCacheImpl' => null, 'metadataDriverImpl' => null, 'proxyDir' => null, - 'allowPartialObjects' => true, //TODO: Remove 'useCExtension' => false, 'namedQueries' => array(), 'namedNativeQueries' => array(), @@ -72,7 +71,7 @@ class Configuration extends \Doctrine\DBAL\Configuration */ public function getAllowPartialObjects() { - return $this->_attributes['allowPartialObjects']; + return true; } /** @@ -86,9 +85,7 @@ class Configuration extends \Doctrine\DBAL\Configuration * @deprecated */ public function setAllowPartialObjects($allowed) - { - $this->_attributes['allowPartialObjects'] = $allowed; - } + {} /** * Sets the directory where Doctrine generates any necessary proxy class files. diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 32698de0b..b0a2e5e16 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -314,19 +314,17 @@ class EntityManager */ public function getReference($entityName, $identifier) { + $class = $this->_metadataFactory->getMetadataFor($entityName); + // Check identity map first, if its already in there just return it. - if ($entity = $this->_unitOfWork->tryGetById($identifier, - $this->_metadataFactory->getMetadataFor($entityName)->rootEntityName)) { + if ($entity = $this->_unitOfWork->tryGetById($identifier, $class->rootEntityName)) { return $entity; } - - if ($this->_config->getAllowPartialObjects()) { - $entity = new $entityName; - $this->getClassMetadata($entityName)->setIdentifierValues($entity, $identifier); - } else { - $entity = $this->_proxyFactory->getReferenceProxy($entityName, $identifier); + if ( ! is_array($identifier)) { + $identifier = array($class->identifier[0] => $identifier); } - $this->_unitOfWork->registerManaged($entity, (array) $identifier, array()); + $entity = $this->_proxyFactory->getReferenceProxy($entityName, $identifier); + $this->_unitOfWork->registerManaged($entity, $identifier, array()); return $entity; } diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index e1051c5ff..766663b52 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -58,8 +58,7 @@ class ObjectHydrator extends AbstractHydrator /** @override */ protected function _prepare() { - $this->_allowPartialObjects = $this->_em->getConfiguration()->getAllowPartialObjects() - || isset($this->_hints[Query::HINT_FORCE_PARTIAL_LOAD]); + $this->_allowPartialObjects = isset($this->_hints[Query::HINT_FORCE_PARTIAL_LOAD]); $this->_proxyFactory = $this->_em->getProxyFactory(); @@ -201,7 +200,7 @@ class ObjectHydrator extends AbstractHydrator foreach ($assoc->targetToSourceKeyColumns as $srcColumn) { $joinColumns[$srcColumn] = $data[$assoc->joinColumnFieldNames[$srcColumn]]; } - if ($assoc->isLazilyFetched()) { + if ($assoc->isLazilyFetched() /*&& ! $assoc->isOptional*/) { // Inject proxy $proxy = $this->_proxyFactory->getAssociationProxy($entity, $assoc, $joinColumns); $this->_uow->setOriginalEntityProperty($oid, $field, $proxy); diff --git a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php index 5de7e4a86..93780ced1 100644 --- a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php +++ b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php @@ -208,7 +208,7 @@ class OneToOneMapping extends AssociationMapping } } - $targetEntity = $em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->load($conditions, $targetEntity); + $targetEntity = $em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->load($conditions, $targetEntity, $this); if ($targetEntity !== null && $targetClass->hasInverseAssociationMapping($this->sourceEntityName, $this->sourceFieldName)) { $targetClass->setFieldValue($targetEntity, @@ -227,7 +227,7 @@ class OneToOneMapping extends AssociationMapping } } - $targetEntity = $em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->load($conditions, $targetEntity); + $targetEntity = $em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->load($conditions, $targetEntity, $this); $targetClass->setFieldValue($targetEntity, $this->mappedByFieldName, $sourceEntity); } diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index 4a16780f4..ecdb99299 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -229,6 +229,7 @@ final class PersistentCollection implements \Doctrine\Common\Collections\Collect private function _initialize() { if ( ! $this->_initialized) { + $this->_coll->clear(); $this->_association->load($this->_owner, $this, $this->_em); $this->_initialized = true; } diff --git a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php index c7cfea833..5225d3978 100644 --- a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php @@ -414,9 +414,9 @@ class StandardEntityPersister * a new entity is created. * @return The loaded entity instance or NULL if the entity/the data can not be found. */ - public function load(array $criteria, $entity = null) + public function load(array $criteria, $entity = null, $assoc = null) { - $stmt = $this->_conn->prepare($this->_getSelectEntitiesSql($criteria)); + $stmt = $this->_conn->prepare($this->_getSelectEntitiesSql($criteria, $assoc)); $stmt->execute(array_values($criteria)); $result = $stmt->fetch(Connection::FETCH_ASSOC); $stmt->closeCursor(); @@ -525,34 +525,31 @@ class StandardEntityPersister $this->_em->getUnitOfWork()->registerManaged($entity, $id, $data); } - if ( ! $this->_em->getConfiguration()->getAllowPartialObjects()) { - // Partial objects not allowed, so make sure we put in proxies and - // empty collections respectively. - foreach ($this->_class->associationMappings as $field => $assoc) { - if ($assoc->isOneToOne()) { - if ($assoc->isLazilyFetched()) { - // Inject proxy - $proxy = $this->_em->getProxyFactory()->getAssociationProxy($entity, $assoc, $joinColumnValues); - $this->_class->reflFields[$field]->setValue($entity, $proxy); - } else { - // Eager load - //TODO: Allow more efficient and configurable batching of these loads - $assoc->load($entity, new $assoc->targetEntityName, $this->_em, $joinColumnValues); - } + // Initialize associations + foreach ($this->_class->associationMappings as $field => $assoc) { + if ($assoc->isOneToOne()) { + if ($assoc->isLazilyFetched()) { + // Inject proxy + $proxy = $this->_em->getProxyFactory()->getAssociationProxy($entity, $assoc, $joinColumnValues); + $this->_class->reflFields[$field]->setValue($entity, $proxy); } else { - // Inject collection - $coll = new PersistentCollection( - $this->_em, - $this->_em->getClassMetadata($assoc->targetEntityName), - /*$this->_class->reflFields[$field]->getValue($entity) ?:*/ new ArrayCollection); - $coll->setOwner($entity, $assoc); - $this->_class->reflFields[$field]->setValue($entity, $coll); - if ($assoc->isLazilyFetched()) { - $coll->setInitialized(false); - } else { - //TODO: Allow more efficient and configurable batching of these loads - $assoc->load($entity, $coll, $this->_em); - } + // Eager load + //TODO: Allow more efficient and configurable batching of these loads + $assoc->load($entity, new $assoc->targetEntityName, $this->_em, $joinColumnValues); + } + } else { + // Inject collection + $coll = new PersistentCollection( + $this->_em, + $this->_em->getClassMetadata($assoc->targetEntityName), + /*$this->_class->reflFields[$field]->getValue($entity) ?:*/ new ArrayCollection); + $coll->setOwner($entity, $assoc); + $this->_class->reflFields[$field]->setValue($entity, $coll); + if ($assoc->isLazilyFetched()) { + $coll->setInitialized(false); + } else { + //TODO: Allow more efficient and configurable batching of these loads + $assoc->load($entity, $coll, $this->_em); } } } @@ -575,13 +572,11 @@ class StandardEntityPersister } $joinColumnNames = array(); - if ( ! $this->_em->getConfiguration()->getAllowPartialObjects()) { - foreach ($this->_class->associationMappings as $assoc) { - if ($assoc->isOwningSide && $assoc->isOneToOne()) { - foreach ($assoc->targetToSourceKeyColumns as $srcColumn) { - $joinColumnNames[] = $srcColumn; - $columnList .= ', ' . $assoc->getQuotedJoinColumnName($srcColumn, $this->_platform); - } + foreach ($this->_class->associationMappings as $assoc2) { + if ($assoc2->isOwningSide && $assoc2->isOneToOne()) { + foreach ($assoc2->targetToSourceKeyColumns as $srcColumn) { + $joinColumnNames[] = $srcColumn; + $columnList .= ', ' . $assoc2->getQuotedJoinColumnName($srcColumn, $this->_platform); } } } @@ -623,12 +618,10 @@ class StandardEntityPersister $columnList .= $this->_class->getQuotedColumnName($field, $this->_platform); } - if ( ! $this->_em->getConfiguration()->getAllowPartialObjects()) { - foreach ($this->_class->associationMappings as $assoc) { - if ($assoc->isOwningSide && $assoc->isOneToOne()) { - foreach ($assoc->targetToSourceKeyColumns as $srcColumn) { - $columnList .= ', ' . $assoc->getQuotedJoinColumnName($srcColumn, $this->_platform); - } + foreach ($this->_class->associationMappings as $assoc) { + if ($assoc->isOwningSide && $assoc->isOneToOne()) { + foreach ($assoc->targetToSourceKeyColumns as $srcColumn) { + $columnList .= ', ' . $assoc->getQuotedJoinColumnName($srcColumn, $this->_platform); } } } diff --git a/lib/Doctrine/ORM/Proxy/Proxy.php b/lib/Doctrine/ORM/Proxy/Proxy.php index 751b9a862..7c354daa6 100644 --- a/lib/Doctrine/ORM/Proxy/Proxy.php +++ b/lib/Doctrine/ORM/Proxy/Proxy.php @@ -8,4 +8,7 @@ namespace Doctrine\ORM\Proxy; * @author Roman Borschel * @since 2.0 */ -interface Proxy {} \ No newline at end of file +interface Proxy +{ + function __isInitialized__(); +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Proxy/ProxyFactory.php b/lib/Doctrine/ORM/Proxy/ProxyFactory.php index f69a21369..135b28457 100644 --- a/lib/Doctrine/ORM/Proxy/ProxyFactory.php +++ b/lib/Doctrine/ORM/Proxy/ProxyFactory.php @@ -289,6 +289,7 @@ namespace { $this->_loaded = true; } } + public function __isInitialized__() { return $this->_loaded; } @@ -331,6 +332,7 @@ namespace { $this->_loaded = true; } } + public function __isInitialized__() { return $this->_loaded; } diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index a85b5a48a..f423f1ca4 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -1575,7 +1575,6 @@ class Parser // Deny hydration of partial objects if doctrine.forcePartialLoad query hint not defined if ( $this->_query->getHydrationMode() == Query::HYDRATE_OBJECT && - ! $this->_em->getConfiguration()->getAllowPartialObjects() && ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) ) { throw DoctrineException::partialObjectsAreDangerous(); diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index d0328cfa5..45b083f15 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -252,7 +252,7 @@ class SqlWalker implements TreeWalker } // LEFT JOIN subclass tables, only if partial objects disallowed - if ( ! $this->_em->getConfiguration()->getAllowPartialObjects() && ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) { + if ( ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) { foreach ($class->subClasses as $subClassName) { $subClass = $this->_em->getClassMetadata($subClassName); $tableAlias = $this->getSqlTableAlias($subClass->primaryTable['name'], $dqlAlias); @@ -446,8 +446,7 @@ class SqlWalker implements TreeWalker ', ', array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions) ); - $addMetaColumns = ! $this->_em->getConfiguration()->getAllowPartialObjects() && - ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) && + $addMetaColumns = ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) && $this->_query->getHydrationMode() == Query::HYDRATE_OBJECT || $this->_query->getHydrationMode() != Query::HYDRATE_OBJECT && @@ -480,6 +479,7 @@ class SqlWalker implements TreeWalker // Add foreign key columns to SQL, if necessary if ($addMetaColumns) { + //FIXME: Include foreign key columns of child classes also!!?? foreach ($class->associationMappings as $assoc) { if ($assoc->isOwningSide && $assoc->isOneToOne()) { if (isset($class->inheritedAssociationFields[$assoc->sourceFieldName])) { @@ -654,22 +654,19 @@ class SqlWalker implements TreeWalker if ($assoc->isOneToOne()) { $sql .= $targetTableName . ' ' . $targetTableAlias . ' ON '; $first = true; - + foreach ($assoc->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) { - if ( ! $first) { - $sql .= ' AND '; - } else { - $first = false; - } + if ( ! $first) $sql .= ' AND '; else $first = false; $quotedSourceColumn = $assoc->getQuotedJoinColumnName($sourceColumn, $this->_platform); - $quotedTargetColumn = $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$targetColumn], $this->_platform); if ($relation->isOwningSide) { + $quotedTargetColumn = $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform); $sql .= $sourceTableAlias . '.' . $quotedSourceColumn . ' = ' . $targetTableAlias . '.' . $quotedTargetColumn; } else { + $quotedTargetColumn = $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$targetColumn], $this->_platform); $sql .= $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $targetTableAlias . '.' . $quotedSourceColumn; @@ -824,8 +821,7 @@ class SqlWalker implements TreeWalker // 1) on Single Table Inheritance: always, since its marginal overhead // 2) on Class Table Inheritance only if partial objects are disallowed, // since it requires outer joining subtables. - if ($class->isInheritanceTypeSingleTable() || ! $this->_em->getConfiguration()->getAllowPartialObjects() - && ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) { + if ($class->isInheritanceTypeSingleTable() || ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) { foreach ($class->subClasses as $subClassName) { $subClass = $this->_em->getClassMetadata($subClassName); foreach ($subClass->fieldMappings as $fieldName => $mapping) { @@ -838,7 +834,7 @@ class SqlWalker implements TreeWalker $sqlTableAlias = $this->getSqlTableAlias($subClass->primaryTable['name'], $dqlAlias); $columnAlias = $this->getSqlColumnAlias($mapping['columnName']); $sql .= $sqlTableAlias . '.' . $subClass->getQuotedColumnName($fieldName, $this->_platform) - . ' AS ' . $columnAlias; + . ' AS ' . $columnAlias; $columnAlias = $this->_platform->getSqlResultCasing($columnAlias); $this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName); diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index bbcc50cdc..c0b276f95 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -559,11 +559,14 @@ class UnitOfWork implements PropertyChangedListener // Look through the entities, and in any of their associations, for transient // enities, recursively. ("Persistence by reachability") if ($assoc->isOneToOne()) { - if ($value instanceof Proxy) { - return; // Ignore proxy objects + if ($value instanceof Proxy && ! $value->__isInitialized__()) { + return; // Ignore uninitialized proxy objects } $value = array($value); + } else if ($value instanceof PersistentCollection) { + $value = $value->unwrap(); } + $targetClass = $this->_em->getClassMetadata($assoc->targetEntityName); foreach ($value as $entry) { $state = $this->getEntityState($entry, self::STATE_NEW); @@ -728,7 +731,7 @@ class UnitOfWork implements PropertyChangedListener $hasPostUpdateListeners = $this->_evm->hasListeners(Events::postUpdate); foreach ($this->_entityUpdates as $oid => $entity) { - if (get_class($entity) == $className) { + if (get_class($entity) == $className || $entity instanceof Proxy && $entity instanceof $className) { if ($hasPreUpdateLifecycleCallbacks) { $class->invokeLifecycleCallbacks(Events::preUpdate, $entity); if ( ! $hasPreUpdateListeners) { diff --git a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php index 91bdc5cbd..1c7321666 100644 --- a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php @@ -352,8 +352,6 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase public function testAddToCollectionDoesNotInitialize() { - $this->_em->getConfiguration()->setAllowPartialObjects(false); - $user = new CmsUser; $user->name = 'Guilherme'; $user->username = 'gblanco'; @@ -382,15 +380,13 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase $gblanco->addPhonenumber($phone); $this->assertFalse($gblanco->getPhonenumbers()->isInitialized()); - + $this->_em->flush(); $this->_em->clear(); $query = $this->_em->createQuery("select u, p from Doctrine\Tests\Models\CMS\CmsUser u join u.phonenumbers p where u.username='gblanco'"); $gblanco2 = $query->getSingleResult(); $this->assertEquals(4, $gblanco2->getPhonenumbers()->count()); - - $this->_em->getConfiguration()->setAllowPartialObjects(true); } public function testSetSetAssociationWithGetReference() @@ -417,7 +413,10 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase // that address to the user without actually loading it, using getReference(). $addressRef = $this->_em->getReference('Doctrine\Tests\Models\CMS\CmsAddress', $address->getId()); - $user->setAddress($addressRef); + //$addressRef->getId(); + //\Doctrine\Common\Util\Debug::dump($addressRef); + + $user->setAddress($addressRef); // Ugh! Initializes address 'cause of $address->setUser($user)! $this->_em->flush(); $this->_em->clear(); @@ -425,7 +424,7 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase // Check with a fresh load that the association is indeed there $query = $this->_em->createQuery("select u, a from Doctrine\Tests\Models\CMS\CmsUser u join u.address a where u.username='gblanco'"); $gblanco = $query->getSingleResult(); - + $this->assertTrue($gblanco instanceof CmsUser); $this->assertTrue($gblanco->getAddress() instanceof CmsAddress); $this->assertEquals('Berlin', $gblanco->getAddress()->getCity()); diff --git a/tests/Doctrine/Tests/ORM/Functional/ManyToManyBidirectionalAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/ManyToManyBidirectionalAssociationTest.php index 775f1c3ab..8e6bf8aaa 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ManyToManyBidirectionalAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ManyToManyBidirectionalAssociationTest.php @@ -5,6 +5,7 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\Tests\Models\ECommerce\ECommerceProduct; use Doctrine\Tests\Models\ECommerce\ECommerceCategory; use Doctrine\ORM\Mapping\AssociationMapping; +use Doctrine\ORM\Query; require_once __DIR__ . '/../../TestInit.php'; @@ -77,7 +78,7 @@ class ManyToManyBidirectionalAssociationTest extends AbstractManyToManyAssociati { $this->_createLoadingFixture(); $products = $this->_findProducts(); - $this->assertLoadingOfOwningSide($products); + $this->assertLoadingOfOwningSide($products); } public function testLazyLoadsCollectionOnTheInverseSide() @@ -128,6 +129,7 @@ class ManyToManyBidirectionalAssociationTest extends AbstractManyToManyAssociati protected function _findProducts() { $query = $this->_em->createQuery('SELECT p, c FROM Doctrine\Tests\Models\ECommerce\ECommerceProduct p LEFT JOIN p.categories c ORDER BY p.id, c.id'); + //$query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true); return $query->getResult(); } diff --git a/tests/Doctrine/Tests/ORM/Functional/MappedSuperclassTest.php b/tests/Doctrine/Tests/ORM/Functional/MappedSuperclassTest.php index 90ab33fa3..fc0b97d6c 100644 --- a/tests/Doctrine/Tests/ORM/Functional/MappedSuperclassTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/MappedSuperclassTest.php @@ -37,7 +37,7 @@ class MappedSuperclassTest extends \Doctrine\Tests\OrmFunctionalTestCase $e2 = $this->_em->find('Doctrine\Tests\ORM\Functional\EntitySubClass', 1); $this->assertEquals(1, $e2->getId()); $this->assertEquals('Roman', $e2->getName()); - $this->assertNull($e2->getMappedRelated1()); + $this->assertTrue($e2->getMappedRelated1() instanceof MappedSuperclassRelated1); $this->assertEquals(42, $e2->getMapped1()); $this->assertEquals('bar', $e2->getMapped2()); } diff --git a/tests/Doctrine/Tests/ORM/Functional/OneToManyBidirectionalAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/OneToManyBidirectionalAssociationTest.php index deea538c9..df3853139 100644 --- a/tests/Doctrine/Tests/ORM/Functional/OneToManyBidirectionalAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/OneToManyBidirectionalAssociationTest.php @@ -107,7 +107,6 @@ class OneToManyBidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctiona public function testLazyLoadsObjectsOnTheInverseSide() { $this->_createFixture(); - $this->_em->getConfiguration()->setAllowPartialObjects(false); $metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceFeature'); $metadata->getAssociationMapping('product')->fetchMode = AssociationMapping::FETCH_LAZY; @@ -118,6 +117,13 @@ class OneToManyBidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctiona $this->assertTrue($product instanceof ECommerceProduct); $this->assertSame('Doctrine Cookbook', $product->getName()); } + + public function testJoinFromOwningSide() + { + $query = $this->_em->createQuery('select f,p from Doctrine\Tests\Models\ECommerce\ECommerceFeature f join f.product p'); + $features = $query->getResult(); + $this->assertEquals(0, count($features)); + } private function _createFixture() { diff --git a/tests/Doctrine/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php index bac488b36..83a14f3f0 100644 --- a/tests/Doctrine/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/OneToOneUnidirectionalAssociationTest.php @@ -5,6 +5,7 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\Tests\Models\ECommerce\ECommerceProduct; use Doctrine\Tests\Models\ECommerce\ECommerceShipping; use Doctrine\ORM\Mapping\AssociationMapping; +use Doctrine\ORM\Query; require_once __DIR__ . '/../../TestInit.php'; @@ -74,9 +75,10 @@ class OneToOneUnidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctiona public function testDoesNotLazyLoadObjectsIfConfigurationDoesNotAllowIt() { $this->_createFixture(); - $this->_em->getConfiguration()->setAllowPartialObjects(true); $query = $this->_em->createQuery('select p from Doctrine\Tests\Models\ECommerce\ECommerceProduct p'); + $query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true); + $result = $query->getResult(); $product = $result[0]; diff --git a/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php b/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php index eb5e1fee2..0d5aa156b 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php @@ -2,6 +2,8 @@ namespace Doctrine\Tests\ORM\Functional; +use Doctrine\ORM\Query; + require_once __DIR__ . '/../../TestInit.php'; /** @@ -49,7 +51,7 @@ class SingleTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->clear(); $query = $this->_em->createQuery("select e from Doctrine\Tests\ORM\Functional\ParentEntity e order by e.data asc"); - + $query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true); $entities = $query->getResult(); $this->assertEquals(2, count($entities)); @@ -64,7 +66,6 @@ class SingleTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->clear(); $query = $this->_em->createQuery("select e from Doctrine\Tests\ORM\Functional\ChildEntity e"); - $entities = $query->getResult(); $this->assertEquals(1, count($entities)); $this->assertTrue($entities[0] instanceof ChildEntity); diff --git a/tests/Doctrine/Tests/ORM/Functional/StandardEntityPersisterTest.php b/tests/Doctrine/Tests/ORM/Functional/StandardEntityPersisterTest.php index 747545bd9..8982b8ecf 100644 --- a/tests/Doctrine/Tests/ORM/Functional/StandardEntityPersisterTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/StandardEntityPersisterTest.php @@ -37,9 +37,11 @@ class StandardEntityPersisterTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->clear(); unset($cart); + $class = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCart'); + $persister = $this->_em->getUnitOfWork()->getEntityPersister('Doctrine\Tests\Models\ECommerce\ECommerceCart'); $newCart = new ECommerceCart(); - $persister->load(array('customer_id' => $customer->getId()), $newCart); + $persister->load(array('customer_id' => $customer->getId()), $newCart, $class->associationMappings['customer']); $this->assertEquals('Credit card', $newCart->getPayment()); } diff --git a/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php b/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php index c2f2a9639..794c62d43 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php @@ -6,6 +6,7 @@ use Doctrine\Tests\Mocks\HydratorMockStatement; use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\ORM\Proxy\ProxyFactory; use Doctrine\ORM\Mapping\AssociationMapping; +use Doctrine\ORM\Query; require_once __DIR__ . '/../../TestInit.php'; @@ -37,7 +38,7 @@ class ObjectHydratorTest extends HydrationTestCase $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $rsm); + $result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true)); $this->assertEquals(2, count($result)); $this->assertTrue($result[0] instanceof \Doctrine\Tests\Models\CMS\CmsUser); @@ -81,7 +82,7 @@ class ObjectHydratorTest extends HydrationTestCase $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $rsm); + $result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true)); $this->assertEquals(4, count($result)); @@ -192,7 +193,7 @@ class ObjectHydratorTest extends HydrationTestCase $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $rsm); + $result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true)); $this->assertEquals(2, count($result)); $this->assertTrue(is_array($result)); @@ -251,7 +252,7 @@ class ObjectHydratorTest extends HydrationTestCase $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $rsm); + $result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true)); $this->assertEquals(2, count($result)); $this->assertTrue(is_array($result)); @@ -316,7 +317,7 @@ class ObjectHydratorTest extends HydrationTestCase $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $rsm); + $result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true)); $this->assertEquals(2, count($result)); $this->assertTrue(is_array($result)); @@ -430,7 +431,7 @@ class ObjectHydratorTest extends HydrationTestCase $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $rsm); + $result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true)); $this->assertEquals(2, count($result)); $this->assertTrue(is_array($result)); @@ -565,7 +566,7 @@ class ObjectHydratorTest extends HydrationTestCase $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $rsm); + $result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true)); $this->assertEquals(2, count($result)); $this->assertTrue(is_array($result)); @@ -672,7 +673,7 @@ class ObjectHydratorTest extends HydrationTestCase $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $result = $hydrator->hydrateAll($stmt, $rsm); + $result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true)); $this->assertEquals(2, count($result)); $this->assertTrue($result[0] instanceof \Doctrine\Tests\Models\Forum\ForumCategory); @@ -710,7 +711,7 @@ class ObjectHydratorTest extends HydrationTestCase $stmt = new HydratorMockStatement($resultSet); $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); - $iterableResult = $hydrator->iterate($stmt, $rsm); + $iterableResult = $hydrator->iterate($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true)); $rowNum = 0; while (($row = $iterableResult->next()) !== false) { diff --git a/tests/Doctrine/Tests/ORM/Performance/HydrationPerformanceTest.php b/tests/Doctrine/Tests/ORM/Performance/HydrationPerformanceTest.php index 8a6469c16..014a99974 100644 --- a/tests/Doctrine/Tests/ORM/Performance/HydrationPerformanceTest.php +++ b/tests/Doctrine/Tests/ORM/Performance/HydrationPerformanceTest.php @@ -6,6 +6,7 @@ require_once __DIR__ . '/../../TestInit.php'; use Doctrine\Tests\Mocks\HydratorMockStatement; use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\Query; /** * Tests to prevent serious performance regressions. @@ -24,7 +25,7 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase * * MAXIMUM TIME: 2 seconds */ - public function testSimpleQueryArrayHydrationPerformance() + public function testSimpleQueryArrayHydrationPerformance10000Rows() { $rsm = new ResultSetMapping; $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); @@ -82,7 +83,7 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase * * MAXIMUM TIME: 3 seconds */ - public function testMixedQueryFetchJoinArrayHydrationPerformance() + public function testMixedQueryFetchJoinArrayHydrationPerformance10000Rows() { $rsm = new ResultSetMapping; $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); @@ -154,7 +155,7 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase * * MAXIMUM TIME: 3 seconds */ - public function testSimpleQueryObjectHydrationPerformance() + public function testSimpleQueryPartialObjectHydrationPerformance10000Rows() { $rsm = new ResultSetMapping; $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); @@ -200,6 +201,62 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase $this->setMaxRunningTime(3); $s = microtime(true); + $result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true)); + $e = microtime(true); + echo __FUNCTION__ . " - " . ($e - $s) . " seconds" . PHP_EOL; + } + + /** + * [romanb: 10000 rows => 3 seconds] + * + * MAXIMUM TIME: 4.5 seconds + */ + public function testSimpleQueryFullObjectHydrationPerformance10000Rows() + { + $rsm = new ResultSetMapping; + $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); + $rsm->addFieldResult('u', 'u__id', 'id'); + $rsm->addFieldResult('u', 'u__status', 'status'); + $rsm->addFieldResult('u', 'u__username', 'username'); + $rsm->addFieldResult('u', 'u__name', 'name'); + + // Faked result set + $resultSet = array( + //row1 + array( + 'u__id' => '1', + 'u__status' => 'developer', + 'u__username' => 'romanb', + 'u__name' => 'Roman', + ), + array( + 'u__id' => '1', + 'u__status' => 'developer', + 'u__username' => 'romanb', + 'u__name' => 'Roman', + ), + array( + 'u__id' => '2', + 'u__status' => 'developer', + 'u__username' => 'romanb', + 'u__name' => 'Roman', + ) + ); + + for ($i = 4; $i < 10000; ++$i) { + $resultSet[] = array( + 'u__id' => $i, + 'u__status' => 'developer', + 'u__username' => 'jwage', + 'u__name' => 'Jonathan', + ); + } + + $stmt = new HydratorMockStatement($resultSet); + $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); + + $this->setMaxRunningTime(4); + $s = microtime(true); $result = $hydrator->hydrateAll($stmt, $rsm); $e = microtime(true); echo __FUNCTION__ . " - " . ($e - $s) . " seconds" . PHP_EOL; @@ -210,7 +267,79 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase * * MAXIMUM TIME: 1 second */ - public function testMixedQueryFetchJoinObjectHydrationPerformance() + public function testMixedQueryFetchJoinPartialObjectHydrationPerformance2000Rows() + { + $rsm = new ResultSetMapping; + $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); + $rsm->addJoinedEntityResult( + 'Doctrine\Tests\Models\CMS\CmsPhonenumber', + 'p', + 'u', + 'phonenumbers' + ); + $rsm->addFieldResult('u', 'u__id', 'id'); + $rsm->addFieldResult('u', 'u__status', 'status'); + $rsm->addFieldResult('u', 'u__username', 'username'); + $rsm->addFieldResult('u', 'u__name', 'name'); + $rsm->addScalarResult('sclr0', 'nameUpper'); + $rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber'); + + // Faked result set + $resultSet = array( + //row1 + array( + 'u__id' => '1', + 'u__status' => 'developer', + 'u__username' => 'romanb', + 'u__name' => 'Roman', + 'sclr0' => 'ROMANB', + 'p__phonenumber' => '42', + ), + array( + 'u__id' => '1', + 'u__status' => 'developer', + 'u__username' => 'romanb', + 'u__name' => 'Roman', + 'sclr0' => 'ROMANB', + 'p__phonenumber' => '43', + ), + array( + 'u__id' => '2', + 'u__status' => 'developer', + 'u__username' => 'romanb', + 'u__name' => 'Roman', + 'sclr0' => 'JWAGE', + 'p__phonenumber' => '91' + ) + ); + + for ($i = 4; $i < 2000; ++$i) { + $resultSet[] = array( + 'u__id' => $i, + 'u__status' => 'developer', + 'u__username' => 'jwage', + 'u__name' => 'Jonathan', + 'sclr0' => 'JWAGE' . $i, + 'p__phonenumber' => '91' + ); + } + + $stmt = new HydratorMockStatement($resultSet); + $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); + + $this->setMaxRunningTime(1); + $s = microtime(true); + $result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true)); + $e = microtime(true); + echo __FUNCTION__ . " - " . ($e - $s) . " seconds" . PHP_EOL; + } + + /** + * [romanb: 2000 rows => 0.6 seconds] + * + * MAXIMUM TIME: 1 second + */ + public function testMixedQueryFetchJoinFullObjectHydrationPerformance200Rows() { $rsm = new ResultSetMapping; $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); @@ -284,7 +413,7 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase * * MAXIMUM TIME: 1 second */ - public function testSimpleQueryScalarHydrationPerformance() + public function testSimpleQueryScalarHydrationPerformance10000Rows() { $rsm = new ResultSetMapping; $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); diff --git a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php index 4cc6dedde..457d80467 100644 --- a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php +++ b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php @@ -1,6 +1,8 @@ _em->createQuery($dql); + $query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true); $query->setDql($dql); foreach ($hints as $key => $value) { @@ -358,13 +361,8 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase */ public function testPartialObjectLoad() { - $oldValue = $this->_em->getConfiguration()->getAllowPartialObjects(); - $this->_em->getConfiguration()->setAllowPartialObjects(false); - $this->parseDql('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u', array( \Doctrine\ORM\Query::HINT_FORCE_PARTIAL_LOAD => false )); - - $this->_em->getConfiguration()->setAllowPartialObjects($oldValue); } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 7bda4a04f..c6ae4fec6 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -2,6 +2,8 @@ namespace Doctrine\Tests\ORM\Query; +use Doctrine\ORM\Query; + require_once __DIR__ . '/../../TestInit.php'; class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase @@ -17,6 +19,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase { try { $query = $this->_em->createQuery($dqlToBeTested); + $query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true); parent::assertEquals($sqlToBeConfirmed, $query->getSql()); $query->free(); } catch (Doctrine_Exception $e) { @@ -274,6 +277,8 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase { // "Get all users who have $phone as a phonenumber." (*cough* doesnt really make sense...) $q1 = $this->_em->createQuery('SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE :param MEMBER OF u.phonenumbers'); + $q1->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true); + $phone = new \Doctrine\Tests\Models\CMS\CmsPhonenumber; $phone->phonenumber = 101; $q1->setParameter('param', $phone); @@ -285,6 +290,8 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase // "Get all users who are members of $group." $q2 = $this->_em->createQuery('SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE :param MEMBER OF u.groups'); + $q2->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true); + $group = new \Doctrine\Tests\Models\CMS\CmsGroup; $group->id = 101; $q2->setParameter('param', $group); @@ -310,20 +317,23 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase public function testSupportsCurrentDateFunction() { - $q = $this->_em->createQuery('SELECT d.id FROM Doctrine\Tests\Models\Generic\DateTimeModel d WHERE d.datetime > current_date()'); - $this->assertEquals('SELECT d0_.id AS id0 FROM date_time_model d0_ WHERE d0_.datetime > CURRENT_DATE', $q->getSql()); + $q = $this->_em->createQuery('SELECT d.id FROM Doctrine\Tests\Models\Generic\DateTimeModel d WHERE d.datetime > current_date()'); + $q->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true); + $this->assertEquals('SELECT d0_.id AS id0 FROM date_time_model d0_ WHERE d0_.datetime > CURRENT_DATE', $q->getSql()); } public function testSupportsCurrentTimeFunction() { - $q = $this->_em->createQuery('SELECT d.id FROM Doctrine\Tests\Models\Generic\DateTimeModel d WHERE d.time > current_time()'); - $this->assertEquals('SELECT d0_.id AS id0 FROM date_time_model d0_ WHERE d0_.time > CURRENT_TIME', $q->getSql()); + $q = $this->_em->createQuery('SELECT d.id FROM Doctrine\Tests\Models\Generic\DateTimeModel d WHERE d.time > current_time()'); + $q->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true); + $this->assertEquals('SELECT d0_.id AS id0 FROM date_time_model d0_ WHERE d0_.time > CURRENT_TIME', $q->getSql()); } public function testSupportsCurrentTimestampFunction() { - $q = $this->_em->createQuery('SELECT d.id FROM Doctrine\Tests\Models\Generic\DateTimeModel d WHERE d.datetime > current_timestamp()'); - $this->assertEquals('SELECT d0_.id AS id0 FROM date_time_model d0_ WHERE d0_.datetime > CURRENT_TIMESTAMP', $q->getSql()); + $q = $this->_em->createQuery('SELECT d.id FROM Doctrine\Tests\Models\Generic\DateTimeModel d WHERE d.datetime > current_timestamp()'); + $q->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true); + $this->assertEquals('SELECT d0_.id AS id0 FROM date_time_model d0_ WHERE d0_.datetime > CURRENT_TIMESTAMP', $q->getSql()); } /*public function testExistsExpressionInWhereCorrelatedSubqueryAssocCondition()