[2.0] Fixed some known issues with inheritance, especially class table inheritance, like join columns not being selected.
This commit is contained in:
parent
ae9a4be658
commit
5b60f87859
@ -186,7 +186,7 @@ class ClassMetadataFactory
|
||||
$class->setVersioned($parent->isVersioned);
|
||||
$class->setVersionField($parent->versionField);
|
||||
$class->setDiscriminatorMap($parent->discriminatorMap);
|
||||
$class->resultColumnNames = $parent->resultColumnNames;
|
||||
$class->setResultColumnNames($parent->resultColumnNames);
|
||||
}
|
||||
|
||||
// Invoke driver
|
||||
@ -227,6 +227,13 @@ class ClassMetadataFactory
|
||||
$this->_generateStaticSql($class);
|
||||
}
|
||||
|
||||
if ($parent) {
|
||||
foreach ($visited as $parentClassName) {
|
||||
$parentClass = $this->_loadedMetadata[$parentClassName];
|
||||
$parentClass->setResultColumnNames(array_merge($parentClass->resultColumnNames, $class->resultColumnNames));
|
||||
}
|
||||
}
|
||||
|
||||
$this->_loadedMetadata[$className] = $class;
|
||||
|
||||
$parent = $class;
|
||||
|
@ -507,6 +507,11 @@ class ClassMetadataInfo
|
||||
{
|
||||
return $this->rootEntityName;
|
||||
}
|
||||
|
||||
public function setResultColumnNames(array $resultColumnNames)
|
||||
{
|
||||
$this->resultColumnNames = $resultColumnNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a field is part of the identifier/primary key field(s).
|
||||
|
@ -309,30 +309,47 @@ class JoinedSubclassPersister extends StandardEntityPersister
|
||||
$this->_class->getQuotedDiscriminatorColumnName($this->_platform);
|
||||
}
|
||||
|
||||
$sql = 'SELECT ' . $columnList . ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $baseTableAlias;
|
||||
|
||||
// INNER JOIN parent tables
|
||||
$joinSql = '';
|
||||
foreach ($this->_class->parentClasses as $parentClassName) {
|
||||
$parentClass = $this->_em->getClassMetadata($parentClassName);
|
||||
$tableAlias = $tableAliases[$parentClassName];
|
||||
$sql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
|
||||
$joinSql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
|
||||
$first = true;
|
||||
foreach ($idColumns as $idColumn) {
|
||||
if ($first) $first = false; else $sql .= ' AND ';
|
||||
$sql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
|
||||
if ($first) $first = false; else $joinSql .= ' AND ';
|
||||
$joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
|
||||
}
|
||||
}
|
||||
|
||||
// OUTER JOIN sub tables
|
||||
foreach ($this->_class->subClasses as $subClassName) {
|
||||
//FIXME: Add columns and foreign key columns of inherited classes to the select list
|
||||
$subClass = $this->_em->getClassMetadata($subClassName);
|
||||
$tableAlias = $tableAliases[$subClassName];
|
||||
$sql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
|
||||
|
||||
// Add subclass columns
|
||||
foreach ($subClass->fieldMappings as $fieldName => $mapping) {
|
||||
if (isset($mapping['inherited'])) {
|
||||
continue;
|
||||
}
|
||||
$columnList .= ', ' . $tableAlias . '.' . $subClass->getQuotedColumnName($fieldName, $this->_platform);
|
||||
}
|
||||
|
||||
// Add join columns (foreign keys)
|
||||
foreach ($subClass->associationMappings as $assoc2) {
|
||||
if ($assoc2->isOwningSide && $assoc2->isOneToOne() && ! isset($subClass->inheritedAssociationFields[$assoc2->sourceFieldName])) {
|
||||
foreach ($assoc2->targetToSourceKeyColumns as $srcColumn) {
|
||||
$columnList .= ', ' . $tableAlias . '.' . $assoc2->getQuotedJoinColumnName($srcColumn, $this->_platform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add LEFT JOIN
|
||||
$joinSql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
|
||||
$first = true;
|
||||
foreach ($idColumns as $idColumn) {
|
||||
if ($first) $first = false; else $sql .= ' AND ';
|
||||
$sql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
|
||||
if ($first) $first = false; else $joinSql .= ' AND ';
|
||||
$joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
|
||||
}
|
||||
}
|
||||
|
||||
@ -350,6 +367,9 @@ class JoinedSubclassPersister extends StandardEntityPersister
|
||||
$conditionSql .= ' = ?';
|
||||
}
|
||||
|
||||
return $sql . ($conditionSql != '' ? ' WHERE ' . $conditionSql : '');
|
||||
return 'SELECT ' . $columnList
|
||||
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $baseTableAlias
|
||||
. $joinSql
|
||||
. ($conditionSql != '' ? ' WHERE ' . $conditionSql : '');
|
||||
}
|
||||
}
|
||||
|
@ -503,7 +503,6 @@ class SqlWalker implements TreeWalker
|
||||
// Add foreign key columns to SQL, if necessary
|
||||
if ($addMetaColumns) {
|
||||
$sqlTableAlias = $this->getSqlTableAlias($class->primaryTable['name'], $dqlAlias);
|
||||
|
||||
foreach ($class->associationMappings as $assoc) {
|
||||
if ($assoc->isOwningSide && $assoc->isOneToOne()) {
|
||||
foreach ($assoc->targetToSourceKeyColumns as $srcColumn) {
|
||||
@ -817,13 +816,14 @@ class SqlWalker implements TreeWalker
|
||||
$this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName);
|
||||
}
|
||||
|
||||
// Add any additional fields of subclasses (not inherited fields)
|
||||
// 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,
|
||||
// since it requires outer joining subtables.
|
||||
if ($class->isInheritanceTypeSingleTable() || ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
|
||||
foreach ($class->subClasses as $subClassName) {
|
||||
$subClass = $this->_em->getClassMetadata($subClassName);
|
||||
$sqlTableAlias = $this->getSqlTableAlias($subClass->primaryTable['name'], $dqlAlias);
|
||||
foreach ($subClass->fieldMappings as $fieldName => $mapping) {
|
||||
if (isset($mapping['inherited'])) {
|
||||
continue;
|
||||
@ -831,7 +831,6 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
if ($beginning) $beginning = false; else $sql .= ', ';
|
||||
|
||||
$sqlTableAlias = $this->getSqlTableAlias($subClass->primaryTable['name'], $dqlAlias);
|
||||
$columnAlias = $this->getSqlColumnAlias($mapping['columnName']);
|
||||
$sql .= $sqlTableAlias . '.' . $subClass->getQuotedColumnName($fieldName, $this->_platform)
|
||||
. ' AS ' . $columnAlias;
|
||||
@ -839,6 +838,20 @@ class SqlWalker implements TreeWalker
|
||||
$columnAlias = $this->_platform->getSqlResultCasing($columnAlias);
|
||||
$this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName);
|
||||
}
|
||||
|
||||
// Add join columns (foreign keys) of the subclass
|
||||
//TODO: Probably better do this in walkSelectClause to honor the INCLUDE_META_COLUMNS hint
|
||||
foreach ($subClass->associationMappings as $fieldName => $assoc) {
|
||||
if ($assoc->isOwningSide && $assoc->isOneToOne() && ! isset($subClass->inheritedAssociationFields[$fieldName])) {
|
||||
foreach ($assoc->targetToSourceKeyColumns as $srcColumn) {
|
||||
if ($beginning) $beginning = false; else $sql .= ', ';
|
||||
$columnAlias = $this->getSqlColumnAlias($srcColumn);
|
||||
$sql .= $sqlTableAlias . '.' . $assoc->getQuotedJoinColumnName($srcColumn, $this->_platform)
|
||||
. ' AS ' . $columnAlias;
|
||||
$this->_rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
33
tests/Doctrine/Tests/Models/Company/CompanyCar.php
Normal file
33
tests/Doctrine/Tests/Models/Company/CompanyCar.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\Company;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="company_cars")
|
||||
*/
|
||||
class CompanyCar
|
||||
{
|
||||
/**
|
||||
* @Id @Column(type="integer")
|
||||
* @GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Column(type="string", length="50")
|
||||
*/
|
||||
private $brand;
|
||||
|
||||
public function __construct($brand = null) {
|
||||
$this->brand = $brand;
|
||||
}
|
||||
|
||||
public function getId() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getBrand() {
|
||||
return $this->title;
|
||||
}
|
||||
}
|
@ -12,6 +12,12 @@ class CompanyManager extends CompanyEmployee
|
||||
* @Column(type="string", length="250")
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @OneToOne(targetEntity="CompanyCar", cascade={"persist"})
|
||||
* @JoinColumn(name="car_id", referencedColumnName="id")
|
||||
*/
|
||||
private $car;
|
||||
|
||||
public function getTitle() {
|
||||
return $this->title;
|
||||
@ -20,4 +26,12 @@ class CompanyManager extends CompanyEmployee
|
||||
public function setTitle($title) {
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getCar() {
|
||||
return $this->car;
|
||||
}
|
||||
|
||||
public function setCar(CompanyCar $car) {
|
||||
$this->car = $car;
|
||||
}
|
||||
}
|
@ -10,7 +10,8 @@ use Doctrine\Tests\Models\Company\CompanyPerson,
|
||||
Doctrine\Tests\Models\Company\CompanyOrganization,
|
||||
Doctrine\Tests\Models\Company\CompanyEvent,
|
||||
Doctrine\Tests\Models\Company\CompanyAuction,
|
||||
Doctrine\Tests\Models\Company\CompanyRaffle;
|
||||
Doctrine\Tests\Models\Company\CompanyRaffle,
|
||||
Doctrine\Tests\Models\Company\CompanyCar;
|
||||
|
||||
/**
|
||||
* Functional tests for the Class Table Inheritance mapping strategy.
|
||||
@ -110,6 +111,27 @@ class ClassTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertTrue(is_numeric($manager->getId()));
|
||||
}
|
||||
|
||||
public function testFindOnBaseClass() {
|
||||
$manager = new CompanyManager;
|
||||
$manager->setName('Roman S. Borschel');
|
||||
$manager->setSalary(100000);
|
||||
$manager->setDepartment('IT');
|
||||
$manager->setTitle('CTO');
|
||||
$this->_em->persist($manager);
|
||||
$this->_em->flush();
|
||||
|
||||
$this->_em->clear();
|
||||
|
||||
$person = $this->_em->find('Doctrine\Tests\Models\Company\CompanyPerson', $manager->getId());
|
||||
|
||||
$this->assertTrue($person instanceof CompanyManager);
|
||||
$this->assertEquals('Roman S. Borschel', $person->getName());
|
||||
$this->assertEquals(100000, $person->getSalary());
|
||||
$this->assertEquals('CTO', $person->getTitle());
|
||||
$this->assertTrue(is_numeric($person->getId()));
|
||||
//$this->assertTrue($person->getCar() instanceof CompanyCar);
|
||||
}
|
||||
|
||||
public function testSelfReferencingOneToOne() {
|
||||
$manager = new CompanyManager;
|
||||
$manager->setName('John Smith');
|
||||
|
@ -308,7 +308,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->_em->getClassMetadata(get_class($person))->setIdentifierValues($person, 101);
|
||||
$q3->setParameter('param', $person);
|
||||
$this->assertEquals(
|
||||
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.title AS title2, c2_.salary AS salary3, c2_.department AS department4, c0_.discr AS discr5, c0_.spouse_id AS spouse_id6 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE EXISTS (SELECT 1 FROM company_persons_friends c3_ INNER JOIN company_persons c4_ ON c3_.person_id = c0_.id WHERE c3_.friend_id = c4_.id AND c4_.id = ?)',
|
||||
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.title AS title2, c1_.car_id AS car_id3, c2_.salary AS salary4, c2_.department AS department5, c0_.discr AS discr6, c0_.spouse_id AS spouse_id7 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE EXISTS (SELECT 1 FROM company_persons_friends c3_ INNER JOIN company_persons c4_ ON c3_.person_id = c0_.id WHERE c3_.friend_id = c4_.id AND c4_.id = ?)',
|
||||
$q3->getSql()
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user