diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index 21140f7a026..a1b0c693e1a 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -43,6 +43,7 @@ * @author Roman Borschel * @author Benjamin Eberlei * @author Stefano Rodriguez + * @author Kévin Dunglas */ class SchemaTool { @@ -151,7 +152,7 @@ public function getSchemaFromMetadata(array $classes) $schema = new Schema(array(), array(), $metadataSchemaConfig); $addedFks = array(); - $blacklistedFks = array(); + $generatedTables = array(); foreach ($classes as $class) { /** @var \Doctrine\ORM\Mapping\ClassMetadata $class */ @@ -160,10 +161,11 @@ public function getSchemaFromMetadata(array $classes) } $table = $schema->createTable($this->quoteStrategy->getTableName($class, $this->platform)); + $generatedTables[] = array('class' => $class, 'table' => $table); if ($class->isInheritanceTypeSingleTable()) { $this->gatherColumns($class, $table); - $this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks); + $this->gatherRelationsSql($class, $table, $schema, $addedFks); // Add the discriminator column $this->addDiscriminatorColumnDefinition($class, $table); @@ -177,7 +179,7 @@ public function getSchemaFromMetadata(array $classes) foreach ($class->subClasses as $subClassName) { $subClass = $this->em->getClassMetadata($subClassName); $this->gatherColumns($subClass, $table); - $this->gatherRelationsSql($subClass, $table, $schema, $addedFks, $blacklistedFks); + $this->gatherRelationsSql($subClass, $table, $schema, $addedFks); $processedClasses[$subClassName] = true; } } elseif ($class->isInheritanceTypeJoined()) { @@ -198,7 +200,7 @@ public function getSchemaFromMetadata(array $classes) } } - $this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks); + $this->gatherRelationsSql($class, $table, $schema, $addedFks); // Add the discriminator column only to the root table if ($class->name == $class->rootEntityName) { @@ -243,7 +245,7 @@ public function getSchemaFromMetadata(array $classes) throw ORMException::notSupported(); } else { $this->gatherColumns($class, $table); - $this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks); + $this->gatherRelationsSql($class, $table, $schema, $addedFks); } $pkColumns = array(); @@ -294,11 +296,49 @@ public function getSchemaFromMetadata(array $classes) ); } } + } - if ($eventManager->hasListeners(ToolEvents::postGenerateSchemaTable)) { + // Add foreign key after full hydration of Table objects + // e.g. allow to check if tables engines support foreign key + $alreadyAddedFk = array(); + $blacklistedFks = array(); + + foreach ($addedFks as $addedFk) { + $theJoinTable = $schema->getTable($addedFk['theJoinTableName']); + + $compositeName = $addedFk['theJoinTableName'].'.'.implode('', $addedFk['localColumns']); + if (isset($alreadyAddedFk[$compositeName]) + && ($addedFk['foreignTableName'] != $alreadyAddedFk[$compositeName]['foreignTableName'] + || 0 < count(array_diff($addedFk['foreignColumns'], $alreadyAddedFk[$compositeName]['foreignColumns']))) + ) { + foreach ($theJoinTable->getForeignKeys() as $fkName => $key) { + if (0 === count(array_diff($key->getLocalColumns(), $addedFk['localColumns'])) + && (($key->getForeignTableName() != $addedFk['foreignTableName']) + || 0 < count(array_diff($key->getForeignColumns(), $addedFk['foreignColumns']))) + ) { + $theJoinTable->removeForeignKey($fkName); + break; + } + } + $blacklistedFks[$compositeName] = true; + } elseif (!isset($blacklistedFks[$compositeName])) { + $alreadyAddedFk[$compositeName] = array('foreignTableName' => $addedFk['foreignTableName'], 'foreignColumns' => $addedFk['foreignColumns']); + + $foreignTable = $schema->getTable($addedFk['foreignTableName']); + $theJoinTable->addForeignKeyConstraint( + $foreignTable, + $addedFk['localColumns'], + $addedFk['foreignColumns'], + $addedFk['fkOptions'] + ); + } + } + + if ($eventManager->hasListeners(ToolEvents::postGenerateSchemaTable)) { + foreach ($generatedTables as $generatedTable) { $eventManager->dispatchEvent( ToolEvents::postGenerateSchemaTable, - new GenerateSchemaTableEventArgs($class, $schema, $table) + new GenerateSchemaTableEventArgs($generatedTable['class'], $schema, $generatedTable['table']) ); } } @@ -474,7 +514,7 @@ private function gatherColumn($class, array $mapping, Table $table) * * @throws \Doctrine\ORM\ORMException */ - private function gatherRelationsSql($class, $table, $schema, &$addedFks, &$blacklistedFks) + private function gatherRelationsSql($class, $table, $schema, &$addedFks) { foreach ($class->associationMappings as $mapping) { if (isset($mapping['inherited'])) { @@ -493,8 +533,7 @@ private function gatherRelationsSql($class, $table, $schema, &$addedFks, &$black $mapping, $primaryKeyColumns, $uniqueConstraints, - $addedFks, - $blacklistedFks + $addedFks ); foreach ($uniqueConstraints as $indexName => $unique) { @@ -521,8 +560,7 @@ private function gatherRelationsSql($class, $table, $schema, &$addedFks, &$black $mapping, $primaryKeyColumns, $uniqueConstraints, - $addedFks, - $blacklistedFks + $addedFks ); // Build second FK constraint (relation table => target table) @@ -533,8 +571,7 @@ private function gatherRelationsSql($class, $table, $schema, &$addedFks, &$black $mapping, $primaryKeyColumns, $uniqueConstraints, - $addedFks, - $blacklistedFks + $addedFks ); $theJoinTable->setPrimaryKey($primaryKeyColumns); @@ -594,7 +631,6 @@ private function getDefiningClass($class, $referencedColumnName) * @param array $primaryKeyColumns * @param array $uniqueConstraints * @param array $addedFks - * @param array $blacklistedFks * * @return void * @@ -607,8 +643,7 @@ private function gatherRelationJoinColumns( $mapping, &$primaryKeyColumns, &$uniqueConstraints, - &$addedFks, - &$blacklistedFks + &$addedFks ) { $localColumns = array(); $foreignColumns = array(); @@ -683,30 +718,13 @@ private function gatherRelationJoinColumns( } } - $compositeName = $theJoinTable->getName().'.'.implode('', $localColumns); - if (isset($addedFks[$compositeName]) - && ($foreignTableName != $addedFks[$compositeName]['foreignTableName'] - || 0 < count(array_diff($foreignColumns, $addedFks[$compositeName]['foreignColumns']))) - ) { - foreach ($theJoinTable->getForeignKeys() as $fkName => $key) { - if (0 === count(array_diff($key->getLocalColumns(), $localColumns)) - && (($key->getForeignTableName() != $foreignTableName) - || 0 < count(array_diff($key->getForeignColumns(), $foreignColumns))) - ) { - $theJoinTable->removeForeignKey($fkName); - break; - } - } - $blacklistedFks[$compositeName] = true; - } elseif (!isset($blacklistedFks[$compositeName])) { - $addedFks[$compositeName] = array('foreignTableName' => $foreignTableName, 'foreignColumns' => $foreignColumns); - $theJoinTable->addUnnamedForeignKeyConstraint( - $foreignTableName, - $localColumns, - $foreignColumns, - $fkOptions - ); - } + $addedFks[] = array( + 'theJoinTableName' => $theJoinTable->getName(), + 'foreignTableName' => $foreignTableName, + 'localColumns' => $localColumns, + 'foreignColumns' => $foreignColumns, + 'fkOptions' => $fkOptions + ); } /** diff --git a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/CompanySchemaTest.php b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/CompanySchemaTest.php index a1e6daca5cf..1b96c3c72da 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/CompanySchemaTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/CompanySchemaTest.php @@ -62,7 +62,8 @@ public function testDropPartSchemaWithForeignKeys() $sql = $this->_schemaTool->getDropSchemaSQL(array( $this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyManager'), + $this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyCar') )); - $this->assertEquals(4, count($sql)); + $this->assertEquals(5, count($sql)); } } diff --git a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php index 4ac97c84bb6..458eaf9ca2c 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php @@ -23,7 +23,7 @@ public function testGetCreateSchemaSql() $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'), $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'), $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsEmail'), - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber'), + $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber') ); $tool = new SchemaTool($this->_em); @@ -85,6 +85,18 @@ public function testGetCreateSchemaSql4() $this->assertEquals(0, count($sql)); } + public function testGetCreateSchemaSql5() + { + $classes = array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\\TableInnodb'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\\TableMyisam') + ); + + $tool = new SchemaTool($this->_em); + $sql = $tool->getCreateSchemaSql($classes); + + $this->assertEquals(3, count($sql)); + } } /** @@ -97,3 +109,25 @@ class MysqlSchemaNamespacedEntity public $id; } +/** + * @Entity + */ +class TableInnodb +{ + /** @Column(type="integer") @Id @GeneratedValue */ + public $id; + /** @OneToOne(targetEntity="TableInnodb") */ + public $selfReferencing; +} + +/** + * @Entity + * @Table(options={"engine"="MyISAM"}) + */ +class TableMyisam +{ + /** @Column(type="integer") @Id @GeneratedValue */ + public $id; + /** @ManyToOne(targetEntity="TableInnodb") */ + public $tableInnodb; +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php index cc4b231d669..db3e952f59d 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php @@ -28,9 +28,11 @@ public function testPostgresMetadataSequenceIncrementedBy10() public function testGetCreateSchemaSql() { $classes = array( - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'), + $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup'), $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'), - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber'), + $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'), + $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsEmail'), + $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber') ); $tool = new SchemaTool($this->_em); @@ -91,9 +93,11 @@ public function testGetCreateSchemaSql3() public function testGetDropSchemaSql() { $classes = array( - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'), + $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup'), $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'), - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber'), + $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'), + $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsEmail'), + $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber') ); $tool = new SchemaTool($this->_em); diff --git a/tests/Doctrine/Tests/ORM/Tools/SchemaToolTest.php b/tests/Doctrine/Tests/ORM/Tools/SchemaToolTest.php index aa80c145d35..71c098e2e64 100644 --- a/tests/Doctrine/Tests/ORM/Tools/SchemaToolTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/SchemaToolTest.php @@ -24,6 +24,7 @@ public function testAddUniqueIndexForUniqueFieldAnnotation() $em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup'), $em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber'), $em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'), + $em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsEmail') ); $schema = $schemaTool->getSchemaFromMetadata($classes); @@ -94,6 +95,7 @@ public function testPostGenerateEvents() $em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup'), $em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber'), $em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'), + $em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsEmail') ); $schema = $schemaTool->getSchemaFromMetadata($classes);