1
0
mirror of synced 2025-02-09 00:39:25 +03:00

Allow association mappings as IDs for joined-table inherited entity

SchemaTool has custom logic for creating the primary key of a
joined-table inherited entity. This logic overlooked association maps
as a possible source for identity columns, resulting in a fatal error
when fetching the primary key list for child entities.

Removed any custom logic for generating primary keys for root entities
in joined-table inheritance, deferring to the common logic used for
other entities.

Also adjusted the child entity logic, scanning association maps for
identity columns, and including the column as appropriate. It also
ensures that the primary key columns are in the correct order.
This commit is contained in:
Tyler Romeo 2017-08-27 02:07:48 -04:00 committed by Marco Pivetta
parent b210c1e364
commit bb8970286d
No known key found for this signature in database
GPG Key ID: 4167D3337FD9D629
5 changed files with 174 additions and 29 deletions

View File

@ -183,19 +183,9 @@ class SchemaTool
} }
} elseif ($class->isInheritanceTypeJoined()) { } elseif ($class->isInheritanceTypeJoined()) {
// Add all non-inherited fields as columns // Add all non-inherited fields as columns
$pkColumns = [];
foreach ($class->fieldMappings as $fieldName => $mapping) { foreach ($class->fieldMappings as $fieldName => $mapping) {
if ( ! isset($mapping['inherited'])) { if ( ! isset($mapping['inherited'])) {
$columnName = $this->quoteStrategy->getColumnName(
$mapping['fieldName'],
$class,
$this->platform
);
$this->gatherColumn($class, $mapping, $table); $this->gatherColumn($class, $mapping, $table);
if ($class->isIdentifier($fieldName)) {
$pkColumns[] = $columnName;
}
} }
} }
@ -206,10 +196,12 @@ class SchemaTool
$this->addDiscriminatorColumnDefinition($class, $table); $this->addDiscriminatorColumnDefinition($class, $table);
} else { } else {
// Add an ID FK column to child tables // Add an ID FK column to child tables
$pkColumns = [];
$inheritedKeyColumns = []; $inheritedKeyColumns = [];
foreach ($class->identifier as $identifierField) { foreach ($class->identifier as $identifierField) {
$idMapping = $class->fieldMappings[$identifierField]; if (isset($class->fieldMappings[$identifierField]['inherited'])) {
if (isset($idMapping['inherited'])) { $idMapping = $class->fieldMappings[$identifierField];
$this->gatherColumn($class, $idMapping, $table); $this->gatherColumn($class, $idMapping, $table);
$columnName = $this->quoteStrategy->getColumnName( $columnName = $this->quoteStrategy->getColumnName(
$identifierField, $identifierField,
@ -221,9 +213,39 @@ class SchemaTool
$pkColumns[] = $columnName; $pkColumns[] = $columnName;
$inheritedKeyColumns[] = $columnName; $inheritedKeyColumns[] = $columnName;
continue;
}
if (isset($class->associationMappings[$identifierField]['inherited'])) {
$idMapping = $class->associationMappings[$identifierField];
$targetEntity = current(
array_filter(
$classes,
function (ClassMetadata $class) use ($idMapping) : bool {
return $class->name === $idMapping['targetEntity'];
}
)
);
foreach ($idMapping['joinColumns'] as $joinColumn) {
if (isset($targetEntity->fieldMappings[$joinColumn['referencedColumnName']])) {
$idMapping = $targetEntity->fieldMappings[$joinColumn['referencedColumnName']];
$columnName = $this->quoteStrategy->getJoinColumnName(
$joinColumn,
$class,
$this->platform
);
$pkColumns[] = $columnName;
$inheritedKeyColumns[] = $columnName;
}
}
} }
} }
if (!empty($inheritedKeyColumns)) {
if ( ! empty($inheritedKeyColumns)) {
// Add a FK constraint on the ID column // Add a FK constraint on the ID column
$table->addForeignKeyConstraint( $table->addForeignKeyConstraint(
$this->quoteStrategy->getTableName( $this->quoteStrategy->getTableName(
@ -236,10 +258,10 @@ class SchemaTool
); );
} }
if ( ! empty($pkColumns)) {
$table->setPrimaryKey($pkColumns);
}
} }
$table->setPrimaryKey($pkColumns);
} elseif ($class->isInheritanceTypeTablePerClass()) { } elseif ($class->isInheritanceTypeTablePerClass()) {
throw ORMException::notSupported(); throw ORMException::notSupported();
} else { } else {
@ -330,7 +352,7 @@ class SchemaTool
} }
} }
if ( ! $this->platform->supportsSchemas() && ! $this->platform->canEmulateSchemas() ) { if ( ! $this->platform->supportsSchemas() && ! $this->platform->canEmulateSchemas()) {
$schema->visit(new RemoveNamespacedAssets()); $schema->visit(new RemoveNamespacedAssets());
} }
@ -496,8 +518,8 @@ class SchemaTool
*/ */
private function gatherRelationsSql($class, $table, $schema, &$addedFks, &$blacklistedFks) private function gatherRelationsSql($class, $table, $schema, &$addedFks, &$blacklistedFks)
{ {
foreach ($class->associationMappings as $mapping) { foreach ($class->associationMappings as $id => $mapping) {
if (isset($mapping['inherited'])) { if (isset($mapping['inherited']) && array_search($id, $class->identifier) === false) {
continue; continue;
} }
@ -633,21 +655,21 @@ class SchemaTool
if ( ! $definingClass) { if ( ! $definingClass) {
throw new \Doctrine\ORM\ORMException( throw new \Doctrine\ORM\ORMException(
"Column name `".$joinColumn['referencedColumnName']."` referenced for relation from ". 'Column name `' . $joinColumn['referencedColumnName'] . '` referenced for relation from '
$mapping['sourceEntity'] . " towards ". $mapping['targetEntity'] . " does not exist." . $mapping['sourceEntity'] . ' towards ' . $mapping['targetEntity'] . ' does not exist.'
); );
} }
$quotedColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); $quotedColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
$quotedRefColumnName = $this->quoteStrategy->getReferencedJoinColumnName( $quotedRefColumnName = $this->quoteStrategy->getReferencedJoinColumnName(
$joinColumn, $joinColumn,
$class, $class,
$this->platform $this->platform
); );
$primaryKeyColumns[] = $quotedColumnName; $primaryKeyColumns[] = $quotedColumnName;
$localColumns[] = $quotedColumnName; $localColumns[] = $quotedColumnName;
$foreignColumns[] = $quotedRefColumnName; $foreignColumns[] = $quotedRefColumnName;
if ( ! $theJoinTable->hasColumn($quotedColumnName)) { if ( ! $theJoinTable->hasColumn($quotedColumnName)) {
// Only add the column to the table if it does not exist already. // Only add the column to the table if it does not exist already.
@ -666,7 +688,7 @@ class SchemaTool
$columnOptions = ['notnull' => false, 'columnDefinition' => $columnDef]; $columnOptions = ['notnull' => false, 'columnDefinition' => $columnDef];
if (isset($joinColumn['nullable'])) { if (isset($joinColumn['nullable'])) {
$columnOptions['notnull'] = !$joinColumn['nullable']; $columnOptions['notnull'] = ! $joinColumn['nullable'];
} }
if (isset($fieldMapping['options'])) { if (isset($fieldMapping['options'])) {
@ -713,7 +735,7 @@ class SchemaTool
} }
} }
$blacklistedFks[$compositeName] = true; $blacklistedFks[$compositeName] = true;
} elseif (!isset($blacklistedFks[$compositeName])) { } elseif ( ! isset($blacklistedFks[$compositeName])) {
$addedFks[$compositeName] = ['foreignTableName' => $foreignTableName, 'foreignColumns' => $foreignColumns]; $addedFks[$compositeName] = ['foreignTableName' => $foreignTableName, 'foreignColumns' => $foreignColumns];
$theJoinTable->addUnnamedForeignKeyConstraint( $theJoinTable->addUnnamedForeignKeyConstraint(
$foreignTableName, $foreignTableName,
@ -820,7 +842,7 @@ class SchemaTool
if ($table->hasPrimaryKey()) { if ($table->hasPrimaryKey()) {
$columns = $table->getPrimaryKey()->getColumns(); $columns = $table->getPrimaryKey()->getColumns();
if (count($columns) == 1) { if (count($columns) == 1) {
$checkSequence = $table->getName() . "_" . $columns[0] . "_seq"; $checkSequence = $table->getName() . '_' . $columns[0] . '_seq';
if ($fullSchema->hasSequence($checkSequence)) { if ($fullSchema->hasSequence($checkSequence)) {
$visitor->acceptSequence($fullSchema->getSequence($checkSequence)); $visitor->acceptSequence($fullSchema->getSequence($checkSequence));
} }

View File

@ -0,0 +1,22 @@
<?php
namespace Doctrine\Tests\Models\CompositeKeyInheritance;
/**
* @Entity
* @Table(name = "joined_derived_child")
*/
class JoinedDerivedChildClass extends JoinedDerivedRootClass
{
/**
* @var string
* @Column(type="string")
*/
public $extension = 'ext';
/**
* @var string
* @Column(type="string")
* @Id
*/
private $additionalId = 'additional';
}

View File

@ -0,0 +1,25 @@
<?php
namespace Doctrine\Tests\Models\CompositeKeyInheritance;
/**
* @Entity
* @Table(name = "joined_derived_identity")
*/
class JoinedDerivedIdentityClass
{
/**
* @var string
* @Column(type="string")
* @Id
*/
protected $id = 'part-0';
/**
* @var JoinedDerivedRootClass[]
* @OneToMany(
* targetEntity="JoinedDerivedRootClass",
* mappedBy="keyPart1"
* )
*/
protected $children;
}

View File

@ -0,0 +1,29 @@
<?php
namespace Doctrine\Tests\Models\CompositeKeyInheritance;
/**
* @Entity
* @Table(name = "joined_derived_root")
* @InheritanceType("JOINED")
* @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({"child" = "JoinedDerivedChildClass", "root" = "JoinedDerivedRootClass"})
*/
class JoinedDerivedRootClass
{
/**
* @var JoinedDerivedIdentityClass
* @ManyToOne(
* targetEntity="JoinedDerivedIdentityClass",
* inversedBy="children"
* )
* @Id
*/
protected $keyPart1 = 'part-1';
/**
* @var string
* @Column(type="string")
* @Id
*/
protected $keyPart2 = 'part-2';
}

View File

@ -14,6 +14,9 @@ use Doctrine\Tests\Models\CMS\CmsEmployee;
use Doctrine\Tests\Models\CMS\CmsGroup; use Doctrine\Tests\Models\CMS\CmsGroup;
use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsPhonenumber;
use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CompositeKeyInheritance\JoinedDerivedChildClass;
use Doctrine\Tests\Models\CompositeKeyInheritance\JoinedDerivedIdentityClass;
use Doctrine\Tests\Models\CompositeKeyInheritance\JoinedDerivedRootClass;
use Doctrine\Tests\Models\Forum\ForumAvatar; use Doctrine\Tests\Models\Forum\ForumAvatar;
use Doctrine\Tests\Models\Forum\ForumUser; use Doctrine\Tests\Models\Forum\ForumUser;
use Doctrine\Tests\Models\NullDefault\NullDefaultColumn; use Doctrine\Tests\Models\NullDefault\NullDefaultColumn;
@ -184,6 +187,50 @@ class SchemaToolTest extends OrmTestCase
$this->assertEquals(255, $column->getLength()); $this->assertEquals(255, $column->getLength());
} }
public function testDerivedCompositeKey() : void
{
$em = $this->_getTestEntityManager();
$schemaTool = new SchemaTool($em);
$schema = $schemaTool->getSchemaFromMetadata(
[
$em->getClassMetadata(JoinedDerivedIdentityClass::class),
$em->getClassMetadata(JoinedDerivedRootClass::class),
$em->getClassMetadata(JoinedDerivedChildClass::class),
]
);
self::assertTrue($schema->hasTable('joined_derived_identity'));
self::assertTrue($schema->hasTable('joined_derived_root'));
self::assertTrue($schema->hasTable('joined_derived_child'));
$rootTable = $schema->getTable('joined_derived_root');
self::assertNotNull($rootTable->getPrimaryKey());
self::assertSame(['keyPart1_id', 'keyPart2'], $rootTable->getPrimaryKey()->getColumns());
$childTable = $schema->getTable('joined_derived_child');
self::assertNotNull($childTable->getPrimaryKey());
self::assertSame(['keyPart1_id', 'keyPart2'], $childTable->getPrimaryKey()->getColumns());
$childTableForeignKeys = $childTable->getForeignKeys();
self::assertCount(2, $childTableForeignKeys);
$expectedColumns = [
'joined_derived_identity' => [['keyPart1_id'], ['id']],
'joined_derived_root' => [['keyPart1_id', 'keyPart2'], ['keyPart1_id', 'keyPart2']],
];
foreach ($childTableForeignKeys as $foreignKey) {
self::assertArrayHasKey($foreignKey->getForeignTableName(), $expectedColumns);
[$localColumns, $foreignColumns] = $expectedColumns[$foreignKey->getForeignTableName()];
self::assertSame($localColumns, $foreignKey->getLocalColumns());
self::assertSame($foreignColumns, $foreignKey->getForeignColumns());
}
}
} }
/** /**