[2.0][DDC-371] Fixed together with other hydration/initialization issues.
This commit is contained in:
parent
457d832f52
commit
536aca23da
@ -23,7 +23,7 @@ class DateTimeType extends Type
|
||||
|
||||
public function convertToDatabaseValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
return ($value !== null)
|
||||
return ($value !== null)
|
||||
? $value->format($platform->getDateTimeFormatString()) : null;
|
||||
}
|
||||
|
||||
|
@ -81,15 +81,20 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$this->_hints['fetched'][$sourceSubclassName][$assoc->sourceFieldName] = true;
|
||||
}
|
||||
}
|
||||
if ($assoc->mappedByFieldName) {
|
||||
$this->_hints['fetched'][$className][$assoc->mappedByFieldName] = true;
|
||||
} else {
|
||||
if (isset($sourceClass->inverseMappings[$className][$assoc->sourceFieldName])) {
|
||||
$inverseAssoc = $sourceClass->inverseMappings[$className][$assoc->sourceFieldName];
|
||||
$this->_hints['fetched'][$className][$inverseAssoc->sourceFieldName] = true;
|
||||
if ($class->subClasses) {
|
||||
foreach ($class->subClasses as $targetSubclassName) {
|
||||
$this->_hints['fetched'][$targetSubclassName][$inverseAssoc->sourceFieldName] = true;
|
||||
if ( ! $assoc->isManyToMany()) {
|
||||
// Mark any non-collection opposite sides as fetched, too.
|
||||
if ($assoc->mappedByFieldName) {
|
||||
$this->_hints['fetched'][$className][$assoc->mappedByFieldName] = true;
|
||||
} else {
|
||||
if (isset($class->inverseMappings[$sourceClassName][$assoc->sourceFieldName])) {
|
||||
$inverseAssoc = $class->inverseMappings[$sourceClassName][$assoc->sourceFieldName];
|
||||
if ($inverseAssoc->isOneToOne()) {
|
||||
$this->_hints['fetched'][$className][$inverseAssoc->sourceFieldName] = true;
|
||||
if ($class->subClasses) {
|
||||
foreach ($class->subClasses as $targetSubclassName) {
|
||||
$this->_hints['fetched'][$targetSubclassName][$inverseAssoc->sourceFieldName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -308,22 +313,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
}
|
||||
} else {
|
||||
$element = $this->_getEntity($data, $dqlAlias);
|
||||
|
||||
// If it's a bi-directional many-to-many, also initialize the reverse collection,
|
||||
// but only once, of course.
|
||||
if ($relation->isManyToMany()) {
|
||||
if ($relation->isOwningSide && isset($this->_ce[$entityName]->inverseMappings[$relation->sourceEntityName][$relationField])) {
|
||||
$inverseFieldName = $this->_ce[$entityName]->inverseMappings[$relation->sourceEntityName][$relationField]->sourceFieldName;
|
||||
if ( ! isset($this->_initializedCollections[spl_object_hash($element) . $inverseFieldName])) {
|
||||
$this->_initRelatedCollection($element, $this->_ce[$entityName], $inverseFieldName);
|
||||
}
|
||||
} else if ($relation->mappedByFieldName) {
|
||||
if ( ! isset($this->_initializedCollections[spl_object_hash($element) . $relation->mappedByFieldName])) {
|
||||
$this->_initRelatedCollection($element, $this->_ce[$entityName], $relation->mappedByFieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
$field = $this->_rsm->indexByMap[$dqlAlias];
|
||||
$indexValue = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
|
||||
@ -359,12 +349,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
// If there is an inverse mapping on the target class its bidirectional
|
||||
if (isset($targetClass->inverseMappings[$relation->sourceEntityName][$relationField])) {
|
||||
$inverseAssoc = $targetClass->inverseMappings[$relation->sourceEntityName][$relationField];
|
||||
if ($inverseAssoc->isOneToMany()) {
|
||||
/*// Only initialize reverse collection if it is not yet initialized.
|
||||
if ( ! isset($this->_initializedCollections[spl_object_hash($element) . $inverseAssoc->sourceFieldName])) {
|
||||
$this->_initRelatedCollection($element, $targetClass, $inverseAssoc->sourceFieldName);
|
||||
}*/
|
||||
} else {
|
||||
if ($inverseAssoc->isOneToOne()) {
|
||||
$targetClass->reflFields[$inverseAssoc->sourceFieldName]->setValue($element, $parentObject);
|
||||
$this->_uow->setOriginalEntityProperty(spl_object_hash($element), $inverseAssoc->sourceFieldName, $parentObject);
|
||||
}
|
||||
|
@ -165,31 +165,23 @@ final class PersistentCollection implements \Doctrine\Common\Collections\Collect
|
||||
/**
|
||||
* INTERNAL:
|
||||
* Adds an element to a collection during hydration. This will automatically
|
||||
* complete bidirectional associations.
|
||||
* complete bidirectional associations in the case of a one-to-many association.
|
||||
*
|
||||
* @param mixed $element The element to add.
|
||||
*/
|
||||
public function hydrateAdd($element)
|
||||
{
|
||||
$this->_coll->add($element);
|
||||
|
||||
// If _backRefFieldName is set, then the association is bidirectional
|
||||
// and we need to set the back reference.
|
||||
if ($this->_backRefFieldName) {
|
||||
if ($this->_backRefFieldName && $this->_association->isOneToMany()) {
|
||||
// Set back reference to owner
|
||||
if ($this->_association->isOneToMany()) {
|
||||
// OneToMany
|
||||
$this->_typeClass->reflFields[$this->_backRefFieldName]
|
||||
->setValue($element, $this->_owner);
|
||||
$this->_em->getUnitOfWork()->setOriginalEntityProperty(
|
||||
spl_object_hash($element),
|
||||
$this->_backRefFieldName,
|
||||
$this->_owner);
|
||||
} else {
|
||||
// ManyToMany
|
||||
$this->_typeClass->reflFields[$this->_backRefFieldName]
|
||||
->getValue($element)->unwrap()->add($this->_owner);
|
||||
}
|
||||
$this->_typeClass->reflFields[$this->_backRefFieldName]
|
||||
->setValue($element, $this->_owner);
|
||||
$this->_em->getUnitOfWork()->setOriginalEntityProperty(
|
||||
spl_object_hash($element),
|
||||
$this->_backRefFieldName,
|
||||
$this->_owner);
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,20 +195,12 @@ final class PersistentCollection implements \Doctrine\Common\Collections\Collect
|
||||
public function hydrateSet($key, $element)
|
||||
{
|
||||
$this->_coll->set($key, $element);
|
||||
|
||||
// If _backRefFieldName is set, then the association is bidirectional
|
||||
// and we need to set the back reference.
|
||||
if ($this->_backRefFieldName) {
|
||||
if ($this->_backRefFieldName && $this->_association->isOneToMany()) {
|
||||
// Set back reference to owner
|
||||
if ($this->_association->isOneToMany()) {
|
||||
// OneToMany
|
||||
$this->_typeClass->reflFields[$this->_backRefFieldName]
|
||||
->setValue($element, $this->_owner);
|
||||
} else {
|
||||
// ManyToMany
|
||||
$this->_typeClass->reflFields[$this->_backRefFieldName]
|
||||
->getValue($element)->set($key, $this->_owner);
|
||||
}
|
||||
$this->_typeClass->reflFields[$this->_backRefFieldName]
|
||||
->setValue($element, $this->_owner);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,10 +44,8 @@ class ManyToManyBidirectionalAssociationTest extends AbstractManyToManyAssociati
|
||||
$this->_em->persist($this->firstProduct);
|
||||
$this->_em->flush();
|
||||
|
||||
$this->assertForeignKeysContain($this->firstProduct->getId(),
|
||||
$this->firstCategory->getId());
|
||||
$this->assertForeignKeysContain($this->firstProduct->getId(),
|
||||
$this->secondCategory->getId());
|
||||
$this->assertForeignKeysContain($this->firstProduct->getId(), $this->firstCategory->getId());
|
||||
$this->assertForeignKeysContain($this->firstProduct->getId(), $this->secondCategory->getId());
|
||||
}
|
||||
|
||||
public function testRemovesAManyToManyAssociation()
|
||||
@ -68,49 +66,20 @@ class ManyToManyBidirectionalAssociationTest extends AbstractManyToManyAssociati
|
||||
$this->assertForeignKeysNotContain($this->firstProduct->getId(), $this->secondCategory->getId());
|
||||
}
|
||||
|
||||
public function testEagerLoadsInverseSide()
|
||||
public function testEagerLoadFromInverseSideAndLazyLoadFromOwningSide()
|
||||
{
|
||||
//$this->_em->getConnection()->getConfiguration()->setSqlLogger(new \Doctrine\DBAL\Logging\EchoSqlLogger);
|
||||
$this->_createLoadingFixture();
|
||||
list ($firstProduct, $secondProduct) = $this->_findProducts();
|
||||
$categories = $firstProduct->getCategories();
|
||||
$this->assertLoadingOfInverseSide($categories);
|
||||
$this->assertLoadingOfInverseSide($secondProduct->getCategories());
|
||||
$categories = $this->_findCategories();
|
||||
$this->assertLazyLoadFromOwningSide($categories);
|
||||
}
|
||||
|
||||
public function testEagerLoadsOwningSide()
|
||||
public function testEagerLoadFromOwningSideAndLazyLoadFromInverseSide()
|
||||
{
|
||||
//$this->_em->getConnection()->getConfiguration()->setSqlLogger(new \Doctrine\DBAL\Logging\EchoSqlLogger);
|
||||
$this->_createLoadingFixture();
|
||||
$products = $this->_findProducts();
|
||||
$this->assertLoadingOfOwningSide($products);
|
||||
}
|
||||
|
||||
public function testLazyLoadsCollectionOnTheInverseSide()
|
||||
{
|
||||
$this->_createLoadingFixture();
|
||||
|
||||
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCategory');
|
||||
$metadata->getAssociationMapping('products')->fetchMode = AssociationMapping::FETCH_LAZY;
|
||||
|
||||
$query = $this->_em->createQuery('SELECT c FROM Doctrine\Tests\Models\ECommerce\ECommerceCategory c order by c.id');
|
||||
$categories = $query->getResult();
|
||||
$this->assertLoadingOfInverseSide($categories);
|
||||
}
|
||||
|
||||
public function testLazyLoadsCollectionOnTheOwningSide()
|
||||
{
|
||||
$this->_createLoadingFixture();
|
||||
|
||||
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct');
|
||||
$metadata->getAssociationMapping('categories')->fetchMode = AssociationMapping::FETCH_LAZY;
|
||||
|
||||
$query = $this->_em->createQuery('SELECT p FROM Doctrine\Tests\Models\ECommerce\ECommerceProduct p order by p.id');
|
||||
$products = $query->getResult();
|
||||
|
||||
$this->assertEquals(2, count($products));
|
||||
$this->assertEquals(0, count($products[0]->getCategories()->unwrap()));
|
||||
$this->assertEquals(0, count($products[1]->getCategories()->unwrap()));
|
||||
|
||||
$this->assertLoadingOfOwningSide($products);
|
||||
$this->assertLazyLoadFromInverseSide($products);
|
||||
}
|
||||
|
||||
private function _createLoadingFixture()
|
||||
@ -130,10 +99,38 @@ class ManyToManyBidirectionalAssociationTest extends AbstractManyToManyAssociati
|
||||
{
|
||||
$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();
|
||||
$result = $query->getResult();
|
||||
$this->assertEquals(2, count($result));
|
||||
$cats1 = $result[0]->getCategories();
|
||||
$cats2 = $result[1]->getCategories();
|
||||
$this->assertTrue($cats1->isInitialized());
|
||||
$this->assertTrue($cats2->isInitialized());
|
||||
$this->assertFalse($cats1[0]->getProducts()->isInitialized());
|
||||
$this->assertFalse($cats2[0]->getProducts()->isInitialized());
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function assertLoadingOfOwningSide($products)
|
||||
protected function _findCategories()
|
||||
{
|
||||
$query = $this->_em->createQuery('SELECT c, p FROM Doctrine\Tests\Models\ECommerce\ECommerceCategory c LEFT JOIN c.products p ORDER BY c.id, p.id');
|
||||
//$query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);
|
||||
$result = $query->getResult();
|
||||
$this->assertEquals(2, count($result));
|
||||
$this->assertTrue($result[0] instanceof ECommerceCategory);
|
||||
$this->assertTrue($result[1] instanceof ECommerceCategory);
|
||||
$prods1 = $result[0]->getProducts();
|
||||
$prods2 = $result[1]->getProducts();
|
||||
$this->assertTrue($prods1->isInitialized());
|
||||
$this->assertTrue($prods2->isInitialized());
|
||||
|
||||
$this->assertFalse($prods1[0]->getCategories()->isInitialized());
|
||||
$this->assertFalse($prods2[0]->getCategories()->isInitialized());
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function assertLazyLoadFromInverseSide($products)
|
||||
{
|
||||
list ($firstProduct, $secondProduct) = $products;
|
||||
|
||||
@ -148,9 +145,17 @@ class ManyToManyBidirectionalAssociationTest extends AbstractManyToManyAssociati
|
||||
|
||||
$firstCategoryProducts = $firstProductCategories[0]->getProducts();
|
||||
$secondCategoryProducts = $firstProductCategories[1]->getProducts();
|
||||
|
||||
$this->assertFalse($firstCategoryProducts->isInitialized());
|
||||
$this->assertFalse($secondCategoryProducts->isInitialized());
|
||||
$this->assertEquals(0, $firstCategoryProducts->unwrap()->count());
|
||||
$this->assertEquals(0, $secondCategoryProducts->unwrap()->count());
|
||||
|
||||
$this->assertEquals(2, count($firstCategoryProducts));
|
||||
$this->assertEquals(2, count($secondCategoryProducts));
|
||||
$this->assertEquals(2, count($firstCategoryProducts)); // lazy-load
|
||||
$this->assertTrue($firstCategoryProducts->isInitialized());
|
||||
$this->assertFalse($secondCategoryProducts->isInitialized());
|
||||
$this->assertEquals(2, count($secondCategoryProducts)); // lazy-load
|
||||
$this->assertTrue($secondCategoryProducts->isInitialized());
|
||||
|
||||
$this->assertTrue($firstCategoryProducts[0] instanceof ECommerceProduct);
|
||||
$this->assertTrue($firstCategoryProducts[1] instanceof ECommerceProduct);
|
||||
@ -160,17 +165,38 @@ class ManyToManyBidirectionalAssociationTest extends AbstractManyToManyAssociati
|
||||
$this->assertCollectionEquals($firstCategoryProducts, $secondCategoryProducts);
|
||||
}
|
||||
|
||||
public function assertLoadingOfInverseSide($categories)
|
||||
public function assertLazyLoadFromOwningSide($categories)
|
||||
{
|
||||
$this->assertTrue($categories[0] instanceof ECommerceCategory);
|
||||
$this->assertTrue($categories[1] instanceof ECommerceCategory);
|
||||
list ($firstCategory, $secondCategory) = $categories;
|
||||
|
||||
$firstCategoryProducts = $firstCategory->getProducts();
|
||||
$secondCategoryProducts = $secondCategory->getProducts();
|
||||
|
||||
$this->assertEquals(2, count($categories[0]->getProducts()));
|
||||
$this->assertTrue($categories[0]->getProducts()->get(0) instanceof ECommerceProduct);
|
||||
$this->assertTrue($categories[0]->getProducts()->get(1) instanceof ECommerceProduct);
|
||||
$this->assertEquals(2, count($firstCategoryProducts));
|
||||
$this->assertEquals(2, count($secondCategoryProducts));
|
||||
|
||||
$this->assertEquals(2, count($categories[1]->getProducts()));
|
||||
$this->assertTrue($categories[1]->getProducts()->get(0) instanceof ECommerceProduct);
|
||||
$this->assertTrue($categories[1]->getProducts()->get(1) instanceof ECommerceProduct);
|
||||
$this->assertTrue($firstCategoryProducts[0] === $secondCategoryProducts[0]);
|
||||
$this->assertTrue($firstCategoryProducts[1] === $secondCategoryProducts[1]);
|
||||
|
||||
$firstProductCategories = $firstCategoryProducts[0]->getCategories();
|
||||
$secondProductCategories = $firstCategoryProducts[1]->getCategories();
|
||||
|
||||
$this->assertFalse($firstProductCategories->isInitialized());
|
||||
$this->assertFalse($secondProductCategories->isInitialized());
|
||||
$this->assertEquals(0, $firstProductCategories->unwrap()->count());
|
||||
$this->assertEquals(0, $secondProductCategories->unwrap()->count());
|
||||
|
||||
$this->assertEquals(2, count($firstProductCategories)); // lazy-load
|
||||
$this->assertTrue($firstProductCategories->isInitialized());
|
||||
$this->assertFalse($secondProductCategories->isInitialized());
|
||||
$this->assertEquals(2, count($secondProductCategories)); // lazy-load
|
||||
$this->assertTrue($secondProductCategories->isInitialized());
|
||||
|
||||
$this->assertTrue($firstProductCategories[0] instanceof ECommerceCategory);
|
||||
$this->assertTrue($firstProductCategories[1] instanceof ECommerceCategory);
|
||||
$this->assertTrue($secondProductCategories[0] instanceof ECommerceCategory);
|
||||
$this->assertTrue($secondProductCategories[1] instanceof ECommerceCategory);
|
||||
|
||||
$this->assertCollectionEquals($firstProductCategories, $secondProductCategories);
|
||||
}
|
||||
}
|
||||
|
@ -74,13 +74,16 @@ class OneToManyBidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctiona
|
||||
$query = $this->_em->createQuery('select p, f from Doctrine\Tests\Models\ECommerce\ECommerceProduct p join p.features f');
|
||||
$result = $query->getResult();
|
||||
$product = $result[0];
|
||||
|
||||
$features = $product->getFeatures();
|
||||
|
||||
$this->assertTrue($features[0] instanceof ECommerceFeature);
|
||||
$this->assertFalse($features[0]->getProduct() instanceof \Doctrine\ORM\Proxy\Proxy);
|
||||
$this->assertSame($product, $features[0]->getProduct());
|
||||
$this->assertEquals('Model writing tutorial', $features[0]->getDescription());
|
||||
$this->assertTrue($features[1] instanceof ECommerceFeature);
|
||||
$this->assertSame($product, $features[1]->getProduct());
|
||||
$this->assertFalse($features[1]->getProduct() instanceof \Doctrine\ORM\Proxy\Proxy);
|
||||
$this->assertEquals('Annotations examples', $features[1]->getDescription());
|
||||
}
|
||||
|
||||
|
69
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC371Test.php
Normal file
69
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC371Test.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\ORM\Query;
|
||||
|
||||
require_once __DIR__ . '/../../../TestInit.php';
|
||||
|
||||
class DDC371Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
//$this->_em->getConnection()->getConfiguration()->setSqlLogger(new \Doctrine\DBAL\Logging\EchoSqlLogger);
|
||||
$this->_schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC371Parent'),
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC371Child')
|
||||
));
|
||||
}
|
||||
|
||||
public function testIssue()
|
||||
{
|
||||
$parent = new DDC371Parent;
|
||||
$parent->data = 'parent';
|
||||
$parent->children = new \Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
$child = new DDC371Child;
|
||||
$child->data = 'child';
|
||||
|
||||
$child->parent = $parent;
|
||||
$parent->children->add($child);
|
||||
|
||||
$this->_em->persist($parent);
|
||||
$this->_em->persist($child);
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$children = $this->_em->createQuery('select c,p from '.__NAMESPACE__.'\DDC371Child c '
|
||||
. 'left join c.parent p where c.id = 1 and p.id = 1')
|
||||
->setHint(Query::HINT_REFRESH, true)
|
||||
->getResult();
|
||||
|
||||
$this->assertEquals(1, count($children));
|
||||
$this->assertFalse($children[0]->parent instanceof \Doctrine\ORM\Proxy\Proxy);
|
||||
$this->assertFalse($children[0]->parent->children->isInitialized());
|
||||
$this->assertEquals(0, $children[0]->parent->children->unwrap()->count());
|
||||
}
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class DDC371Child {
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private $id;
|
||||
/** @Column(type="string") */
|
||||
public $data;
|
||||
/** @ManyToOne(targetEntity="DDC371Parent") @JoinColumn(name="parentId") */
|
||||
public $parent;
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class DDC371Parent {
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private $id;
|
||||
/** @Column(type="string") */
|
||||
public $data;
|
||||
/** @OneToMany(targetEntity="DDC371Child", mappedBy="parent") */
|
||||
public $children;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user