diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index a9697c15a..4bc53f738 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -195,7 +195,7 @@ abstract class AbstractHydrator $cache[$key]['fieldName'] = $fieldName; $cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key]; $classMetadata = $this->_em->getClassMetadata($this->_rsm->aliasMap[$cache[$key]['dqlAlias']]); - $cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName); + $cache[$key]['isIdentifier'] = isset($this->_rsm->isIdentifierColumn[$cache[$key]['dqlAlias']][$key]); } } diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 454078dfe..5ce7080c2 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -989,7 +989,7 @@ class BasicEntityPersister $columnAlias = $srcColumn . $this->_sqlAliasCounter++; $columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) ) . ".$srcColumn AS $columnAlias"; $resultColumnName = $this->_platform->getSQLResultCasing($columnAlias); - $this->_rsm->addMetaResult($alias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn); + $this->_rsm->addMetaResult($alias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, isset($assoc['id']) && $assoc['id'] === true); } } return $columnList; diff --git a/lib/Doctrine/ORM/Query/ResultSetMapping.php b/lib/Doctrine/ORM/Query/ResultSetMapping.php index b10b0d15d..3a086e2cd 100644 --- a/lib/Doctrine/ORM/Query/ResultSetMapping.php +++ b/lib/Doctrine/ORM/Query/ResultSetMapping.php @@ -112,6 +112,13 @@ class ResultSetMapping * @var array */ public $declaringClasses = array(); + + /** + * This is necessary to hydrate derivate foreign keys correctly. + * + * @var array + */ + public $isIdentifierColumn = array(); /** * Adds an entity result to this ResultSetMapping. @@ -381,13 +388,17 @@ class ResultSetMapping /** * Adds a meta column (foreign key or discriminator column) to the result set. * - * @param $alias - * @param $columnName - * @param $fieldName + * @param string $alias + * @param string $columnName + * @param string $fieldName + * @param bool */ - public function addMetaResult($alias, $columnName, $fieldName) + public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false) { $this->metaMappings[$columnName] = $fieldName; $this->columnOwnerMap[$columnName] = $alias; + if ($isIdentifierColumn) { + $this->isIdentifierColumn[$alias][$columnName] = true; + } } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 0b5cfbef0..611cd0a1d 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -576,7 +576,7 @@ class SqlWalker implements TreeWalker $columnAlias = $this->getSQLColumnAlias($srcColumn); $sql .= ", $sqlTableAlias." . $srcColumn . ' AS ' . $columnAlias; $columnAlias = $this->_platform->getSQLResultCasing($columnAlias); - $this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn); + $this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, (isset($assoc['id']) && $assoc['id'] === true)); } } } @@ -591,7 +591,7 @@ class SqlWalker implements TreeWalker $columnAlias = $this->getSQLColumnAlias($srcColumn); $sql .= ', ' . $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias; $columnAlias = $this->_platform->getSQLResultCasing($columnAlias); - $this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn); + $this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, (isset($assoc['id']) && $assoc['id'] === true)); } } } @@ -1021,29 +1021,6 @@ class SqlWalker implements TreeWalker $this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name); } - if ($class->containsForeignIdentifier) { - // Add double entry for association identifier columns to simplify hydrator code - foreach ($class->identifier AS $idField) { - if (isset($class->associationMappings[$idField])) { - if (isset($mapping['inherited'])) { - $tableName = $this->_em->getClassMetadata($mapping['inherited'])->table['name']; - } else { - $tableName = $class->table['name']; - } - - if ($beginning) $beginning = false; else $sql .= ', '; - - $joinColumnName = $class->associationMappings[$idField]['joinColumns'][0]['name']; - $sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias); - $columnAlias = $this->getSQLColumnAlias($joinColumnName); - $sql .= $sqlTableAlias . '.' . $joinColumnName . ' AS ' . $columnAlias; - - $columnAlias = $this->_platform->getSQLResultCasing($columnAlias); - $this->_rsm->addMetaResult($dqlAlias, $columnAlias, $idField); - } - } - } - // Add any additional fields of subclasses (excluding inherited fields) // 1) on Single Table Inheritance: always, since its marginal overhead // 2) on Class Table Inheritance only if partial objects are disallowed, diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1080Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1080Test.php new file mode 100644 index 000000000..50b02e350 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1080Test.php @@ -0,0 +1,317 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1080Foo'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1080Bar'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1080FooBar'), + )); + + $foo1 = new DDC1080Foo(); + $foo1->setFooTitle('foo title 1'); + $foo2 = new DDC1080Foo(); + $foo2->setFooTitle('foo title 2'); + + $bar1 = new DDC1080Bar(); + $bar1->setBarTitle('bar title 1'); + $bar2 = new DDC1080Bar(); + $bar2->setBarTitle('bar title 2'); + $bar3 = new DDC1080Bar(); + $bar3->setBarTitle('bar title 3'); + + $foobar1 = new DDC1080FooBar(); + $foobar1->setFoo($foo1); + $foobar1->setBar($bar1); + $foobar1->setOrderNr(0); + + $foobar2 = new DDC1080FooBar(); + $foobar2->setFoo($foo1); + $foobar2->setBar($bar2); + $foobar2->setOrderNr(0); + + $foobar3 = new DDC1080FooBar(); + $foobar3->setFoo($foo1); + $foobar3->setBar($bar3); + $foobar3->setOrderNr(0); + + $this->_em->persist($foo1); + $this->_em->persist($foo2); + $this->_em->persist($bar1); + $this->_em->persist($bar2); + $this->_em->persist($bar3); + $this->_em->flush(); + + $this->_em->persist($foobar1); + $this->_em->persist($foobar2); + $this->_em->persist($foobar3); + $this->_em->flush(); + $this->_em->clear(); + + $foo = $this->_em->find('Doctrine\Tests\ORM\Functional\Ticket\DDC1080Foo', $foo1->getFooId()); + $fooBars = $foo->getFooBars(); + + $this->assertEquals(3, count($fooBars), "Should return three foobars."); + } +} + + +/** + * @Entity + * @Table(name="foo") + */ +class DDC1080Foo +{ + + /** + * @Id + * @Column(name="fooID", type="integer") + * @GeneratedValue(strategy="AUTO") + */ + protected $_fooID; + /** + * @Column(name="fooTitle", type="string") + */ + protected $_fooTitle; + /** + * @OneToMany(targetEntity="DDC1080FooBar", mappedBy="_foo", + * cascade={"persist"}) + * @OrderBy({"_orderNr"="ASC"}) + */ + protected $_fooBars; + + public function __construct() + { + $this->_fooBars = new \Doctrine\Common\Collections\ArrayCollection(); + } + + /** + * @return the $fooID + */ + public function getFooID() + { + return $this->_fooID; + } + + /** + * @return the $fooTitle + */ + public function getFooTitle() + { + return $this->_fooTitle; + } + + /** + * @return the $fooBars + */ + public function getFooBars() + { + return $this->_fooBars; + } + + /** + * @param field_type $fooID + */ + public function setFooID($fooID) + { + $this->_fooID = $fooID; + } + + /** + * @param field_type $fooTitle + */ + public function setFooTitle($fooTitle) + { + $this->_fooTitle = $fooTitle; + } + + /** + * @param field_type $fooBars + */ + public function setFooBars($fooBars) + { + $this->_fooBars = $fooBars; + } + +} +/** + * @Entity + * @Table(name="bar") + */ +class DDC1080Bar +{ + + /** + * @Id + * @Column(name="barID", type="integer") + * @GeneratedValue(strategy="AUTO") + */ + protected $_barID; + /** + * @Column(name="barTitle", type="string") + */ + protected $_barTitle; + /** + * @OneToMany(targetEntity="DDC1080FooBar", mappedBy="_bar", + * cascade={"persist"}) + * @OrderBy({"_orderNr"="ASC"}) + */ + protected $_fooBars; + + public function __construct() + { + $this->_fooBars = new \Doctrine\Common\Collections\ArrayCollection(); + } + + /** + * @return the $barID + */ + public function getBarID() + { + return $this->_barID; + } + + /** + * @return the $barTitle + */ + public function getBarTitle() + { + return $this->_barTitle; + } + + /** + * @return the $fooBars + */ + public function getFooBars() + { + return $this->_fooBars; + } + + /** + * @param field_type $barID + */ + public function setBarID($barID) + { + $this->_barID = $barID; + } + + /** + * @param field_type $barTitle + */ + public function setBarTitle($barTitle) + { + $this->_barTitle = $barTitle; + } + + /** + * @param field_type $fooBars + */ + public function setFooBars($fooBars) + { + $this->_fooBars = $fooBars; + } + +} + +/** + * @Table(name="fooBar") + * @Entity + */ +class DDC1080FooBar +{ + + /** + * @ManyToOne(targetEntity="DDC1080Foo") + * @JoinColumn(name="fooID", referencedColumnName="fooID") + * @Id + */ + protected $_foo = null; + /** + * @ManyToOne(targetEntity="DDC1080Bar") + * @JoinColumn(name="barID", referencedColumnName="barID") + * @Id + */ + protected $_bar = null; + /** + * @var integer orderNr + * @Column(name="orderNr", type="integer", nullable=false) + */ + protected $_orderNr = null; + + /** + * Retrieve the foo property + * + * @return DDC1080Foo + */ + public function getFoo() + { + return $this->_foo; + } + + /** + * Set the foo property + * + * @param DDC1080Foo $foo + * @return DDC1080FooBar + */ + public function setFoo($foo) + { + $this->_foo = $foo; + return $this; + } + + /** + * Retrieve the bar property + * + * @return DDC1080Bar + */ + public function getBar() + { + return $this->_bar; + } + + /** + * Set the bar property + * + * @param DDC1080Bar $bar + * @return DDC1080FooBar + */ + public function setBar($bar) + { + $this->_bar = $bar; + return $this; + } + + /** + * Retrieve the orderNr property + * + * @return integer|null + */ + public function getOrderNr() + { + return $this->_orderNr; + } + + /** + * Set the orderNr property + * + * @param integer|null $orderNr + * @return DDC1080FooBar + */ + public function setOrderNr($orderNr) + { + $this->_orderNr = $orderNr; + return $this; + } + +} +