From 556f8699ee5e622f0d2375b209cd2e59b3390b55 Mon Sep 17 00:00:00 2001 From: beberlei Date: Fri, 4 Dec 2009 21:40:03 +0000 Subject: [PATCH] [2.0] DDC-186 - Fixed DatabaseDriver to work with new Schema abstraction, added functional test-cases for database to yaml convertion. --- .../DBAL/Platforms/AbstractPlatform.php | 50 ++++++++++---- .../DBAL/Schema/AbstractSchemaManager.php | 15 +++- .../ORM/Mapping/Driver/DatabaseDriver.php | 57 +++++++++++----- .../SchemaManagerFunctionalTestCase.php | 28 +++++++- .../Tests/ORM/Functional/AllTests.php | 1 + .../ORM/Functional/DatabaseDriver/fkYaml.yml | 26 +++++++ .../Functional/DatabaseDriver/simpleYaml.yml | 17 +++++ .../ORM/Functional/DatabaseDriverTest.php | 68 +++++++++++++++++++ .../Doctrine/Tests/OrmFunctionalTestCase.php | 4 ++ 9 files changed, 232 insertions(+), 34 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/DatabaseDriver/fkYaml.yml create mode 100644 tests/Doctrine/Tests/ORM/Functional/DatabaseDriver/simpleYaml.yml create mode 100644 tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php diff --git a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php index c260249a3..8ea03fc58 100644 --- a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -44,6 +44,16 @@ use Doctrine\DBAL\DBALException, */ abstract class AbstractPlatform { + /** + * @var int + */ + const CREATE_INDEXES = 1; + + /** + * @var int + */ + const CREATE_FOREIGNKEYS = 2; + /** * Constructor. */ @@ -530,29 +540,34 @@ abstract class AbstractPlatform * on this platform. * * @param string $table The name of the table. - * @param array $columns The column definitions for the table. - * @param array $options The table constraints. + * @param int $createFlags * @return array The sequence of SQL statements. */ - public function getCreateTableSql(Table $table) + public function getCreateTableSql(Table $table, $createFlags=self::CREATE_INDEXES) { + if (!is_int($createFlags)) { + throw new \InvalidArgumentException("Second argument of AbstractPlatform::getCreateTableSql() has to be integer."); + } + $tableName = $table->getName(); $options = $table->getOptions(); $options['uniqueConstraints'] = array(); $options['indexes'] = array(); $options['primary'] = array(); - foreach($table->getIndexes() AS $index) { - /* @var $index Index */ - if($index->isPrimary()) { - $options['primary'] = $index->getColumns(); - } else { - $options['indexes'][$index->getName()] = $index; + if (($createFlags&self::CREATE_INDEXES) > 0) { + foreach ($table->getIndexes() AS $index) { + /* @var $index Index */ + if ($index->isPrimary()) { + $options['primary'] = $index->getColumns(); + } else { + $options['indexes'][$index->getName()] = $index; + } } } $columns = array(); - foreach($table->getColumns() AS $column) { + foreach ($table->getColumns() AS $column) { /* @var \Doctrine\DBAL\Schema\Column $column */ $columnData = array(); $columnData['name'] = $column->getName(); @@ -580,16 +595,23 @@ abstract class AbstractPlatform $columns[$columnData['name']] = $columnData; } + if (($createFlags&self::CREATE_FOREIGNKEYS) > 0) { + $options['foreignKeys'] = array(); + foreach ($table->getForeignKeys() AS $fkConstraint) { + $options['foreignKeys'][] = $fkConstraint; + } + } + return $this->_getCreateTableSql($tableName, $columns, $options); } /** - * @param string $table + * @param string $tableName * @param array $columns * @param array $options * @return array */ - protected function _getCreateTableSql($table, array $columns, array $options = array()) + protected function _getCreateTableSql($tableName, array $columns, array $options = array()) { $columnListSql = $this->getColumnDeclarationListSql($columns); @@ -609,7 +631,7 @@ abstract class AbstractPlatform } } - $query = 'CREATE TABLE ' . $table . ' (' . $columnListSql; + $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql; $check = $this->getCheckDeclarationSql($columns); if ( ! empty($check)) { @@ -621,7 +643,7 @@ abstract class AbstractPlatform if (isset($options['foreignKeys'])) { foreach ((array) $options['foreignKeys'] AS $definition) { - $sql[] = $this->getCreateForeignKeySql($definition, $name); + $sql[] = $this->getCreateForeignKeySql($definition, $tableName); } } diff --git a/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php b/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php index b30e5191f..884728318 100644 --- a/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php @@ -24,6 +24,7 @@ namespace Doctrine\DBAL\Schema; use \Doctrine\DBAL\Types; use \Doctrine\Common\DoctrineException; use \Doctrine\DBAL\DBALException; +use \Doctrine\DBAL\Platforms\AbstractPlatform; /** * Base class for schema managers. Schema managers are used to inspect and/or @@ -65,6 +66,16 @@ abstract class AbstractSchemaManager $this->_platform = $this->_conn->getDatabasePlatform(); } + /** + * Return associated platform. + * + * @return \Doctrine\DBAL\Platform\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_platform; + } + /** * Try any method on the schema manager. Normally a method throws an * exception when your DBMS doesn't support it or if an error occurs. @@ -410,10 +421,12 @@ abstract class AbstractSchemaManager * Create a new table. * * @param Table $table + * @param int $createFlags */ public function createTable(Table $table) { - $this->_execSql($this->_platform->getCreateTableSql($table)); + $createFlags = AbstractPlatform::CREATE_INDEXES|AbstractPlatform::CREATE_FOREIGNKEYS; + $this->_execSql($this->_platform->getCreateTableSql($table, $createFlags)); } /** diff --git a/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php b/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php index adc82214a..e9b798ed8 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php @@ -68,35 +68,42 @@ class DatabaseDriver implements Driver $metadata->primaryTable['name'] = $tableName; $columns = $this->_sm->listTableColumns($tableName); - try { + + if($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) { $foreignKeys = $this->_sm->listTableForeignKeys($tableName); - } catch (\Doctrine\Common\DoctrineException $e) { + } else { $foreignKeys = array(); } + $indexes = $this->_sm->listTableIndexes($tableName); + $ids = array(); $fieldMappings = array(); foreach ($columns as $column) { // Skip columns that are foreign keys foreach ($foreignKeys as $foreignKey) { - if ($column['name'] == $foreignKey['local']) { + if (in_array($column->getName(), $foreignKey->getColumns())) { continue(2); } } $fieldMapping = array(); - if ($column['primary']) { + if (in_array($column->getName(), $indexes['primary']->getColumns())) { $fieldMapping['id'] = true; } - $fieldMapping['fieldName'] = Inflector::camelize($column['name']); - $fieldMapping['columnName'] = $column['name']; - $fieldMapping['type'] = strtolower((string) $column['type']); - $fieldMapping['length'] = $column['length']; - $fieldMapping['unsigned'] = $column['unsigned']; - $fieldMapping['fixed'] = $column['fixed']; - $fieldMapping['notnull'] = $column['notnull']; - $fieldMapping['default'] = $column['default']; + $fieldMapping['fieldName'] = Inflector::camelize($column->getName()); + $fieldMapping['columnName'] = $column->getName(); + $fieldMapping['type'] = strtolower((string) $column->getType()); + + if ($column->getType() instanceof \Doctrine\DBAL\Types\StringType) { + $fieldMapping['length'] = $column->getLength(); + $fieldMapping['fixed'] = $column->getFixed(); + } else if ($column->getType() instanceof \Doctrine\DBAL\Types\IntegerType) { + $fieldMapping['unsigned'] = $column->getUnsigned(); + } + $fieldMapping['notnull'] = $column->getNotNull(); + $fieldMapping['default'] = $column->getDefault(); if (isset($fieldMapping['id'])) { $ids[] = $fieldMapping; @@ -120,13 +127,27 @@ class DatabaseDriver implements Driver } foreach ($foreignKeys as $foreignKey) { + if (count($foreignKey->getColumns()) != 1) { + throw new MappingException( + "Cannot generate mapping for table '".$tableName."' with foreign keys with multiple local columns." + ); + } + $localColumn = current($foreignKey->getColumns()); + + if (count($foreignKey->getForeignColumns()) != 1) { + throw new MappingException( + "Cannot generate mapping for table '".$tableName."' with foreign keys with multiple foreign columns." + ); + } + $foreignColumn = current($foreignKey->getForeignColumns()); + $associationMapping = array(); - $associationMapping['fieldName'] = Inflector::camelize(str_replace('_id', '', $foreignKey['local'])); - $associationMapping['columnName'] = $foreignKey['local']; - $associationMapping['targetEntity'] = Inflector::classify($foreignKey['table']); + $associationMapping['fieldName'] = Inflector::camelize(str_replace('_id', '', $localColumn)); + $associationMapping['columnName'] = $localColumn; + $associationMapping['targetEntity'] = Inflector::classify($foreignKey->getForeignTableName()); $associationMapping['joinColumns'][] = array( - 'name' => $foreignKey['local'], - 'referencedColumnName' => $foreignKey['foreign'] + 'name' => $localColumn, + 'referencedColumnName' => $foreignColumn ); $metadata->mapManyToOne($associationMapping); @@ -156,7 +177,7 @@ class DatabaseDriver implements Driver { $tables = array(); foreach ($this->_sm->listTables() as $table) { - $tables[] = $table; + $tables[] = $table->getName(); } return $tables; diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php index c9a234cf0..c4b39d32a 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php @@ -2,7 +2,8 @@ namespace Doctrine\Tests\DBAL\Functional\Schema; -use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Types\Type, + Doctrine\DBAL\Schema\AbstractSchemaManager; require_once __DIR__ . '/../../../TestInit.php'; @@ -197,6 +198,31 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest $this->assertFalse($tableIndexes['test']->isPrimary()); } + public function testCreateTableWithForeignKeys() + { + if(!$this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) { + $this->markTestSkipped('Platform does not support foreign keys.'); + } + + $tableB = $this->getTestTable('test_foreign'); + + $this->_sm->dropAndCreateTable($tableB); + + $tableA = $this->getTestTable('test_create_fk'); + $tableA->addForeignKeyConstraint('test_foreign', array('foreign_key_test'), array('id')); + + $this->_sm->dropAndCreateTable($tableA); + + $fkConstraints = $this->_sm->listTableForeignKeys('test_create_fk'); + $this->assertEquals(1, count($fkConstraints)); + + $fkConstraint = current($fkConstraints); + $fkConstraint->setCaseMode("lower"); + $this->assertEquals('test_foreign', $fkConstraint->getForeignTableName()); + $this->assertEquals(array('foreign_key_test'), $fkConstraint->getColumns()); + $this->assertEquals(array('id'), $fkConstraint->getForeignColumns()); + } + public function testListForeignKeys() { if(!$this->_conn->getDatabasePlatform()->supportsForeignKeyConstraints()) { diff --git a/tests/Doctrine/Tests/ORM/Functional/AllTests.php b/tests/Doctrine/Tests/ORM/Functional/AllTests.php index f2fe6cac5..c110476c5 100644 --- a/tests/Doctrine/Tests/ORM/Functional/AllTests.php +++ b/tests/Doctrine/Tests/ORM/Functional/AllTests.php @@ -45,6 +45,7 @@ class AllTests $suite->addTestSuite('Doctrine\Tests\ORM\Functional\MappedSuperclassTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\EntityRepositoryTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\IdentityMapTest'); + $suite->addTestSuite('Doctrine\Tests\ORM\Functional\DatabaseDriverTest'); $suite->addTest(Locking\AllTests::suite()); $suite->addTest(SchemaTool\AllTests::suite()); diff --git a/tests/Doctrine/Tests/ORM/Functional/DatabaseDriver/fkYaml.yml b/tests/Doctrine/Tests/ORM/Functional/DatabaseDriver/fkYaml.yml new file mode 100644 index 000000000..4cd062bcd --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/DatabaseDriver/fkYaml.yml @@ -0,0 +1,26 @@ +DbdriverBaz: + type: entity + table: dbdriver_baz + fields: + id: + id: true + type: integer + unsigned: false + notnull: true + default: null + generator: + strategy: AUTO + oneToOne: + bar: + targetEntity: DbdriverBar + cascade: + remove: false + persist: false + refresh: false + merge: false + detach: false + mappedBy: null + joinColumns: + bar_id: + referencedColumnName: id + orphanRemoval: false \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/DatabaseDriver/simpleYaml.yml b/tests/Doctrine/Tests/ORM/Functional/DatabaseDriver/simpleYaml.yml new file mode 100644 index 000000000..2a974a64a --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/DatabaseDriver/simpleYaml.yml @@ -0,0 +1,17 @@ +DbdriverFoo: + type: entity + table: dbdriver_foo + fields: + id: + id: true + type: integer + unsigned: false + notnull: true + default: null + generator: + strategy: AUTO + bar: + type: string(200) + fixed: false + notnull: true + default: null \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php b/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php new file mode 100644 index 000000000..b3f562a1a --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/DatabaseDriverTest.php @@ -0,0 +1,68 @@ +createColumn('id', 'integer'); + $table->setPrimaryKey(array('id')); + $table->createColumn('bar', 'string', array('length' => 200)); + + $this->_sm = $this->_em->getConnection()->getSchemaManager(); + $this->_sm->dropAndCreateTable($table); + + $this->assertClassMetadataYamlEqualsFile(__DIR__."/DatabaseDriver/simpleYaml.yml", "DbdriverFoo"); + } + + protected function assertClassMetadataYamlEqualsFile($file, $className) + { + $cm = new ClassMetadataExporter(); + $cm->addMappingSource($this->_sm, 'database'); + $exporter = $cm->getExporter('yaml'); + $metadatas = $cm->getMetadatasForMappingSources(); + + $output = false; + foreach ($metadatas AS $metadata) { + if ($metadata->name == $className) { + $output = $exporter->exportClassMetadata($metadata); + } + } + + $this->assertTrue($output!==false, "No class matching the name '".$className."' was found!"); + $this->assertEquals(strtolower(trim(file_get_contents($file))), strtolower(trim($output))); + } + + public function testCreateYamlWithForeignKeyFromDatabase() + { + if (!$this->_em->getConnection()->getDatabasePlatform()->supportsForeignKeyConstraints()) { + $this->markTestSkipped('Platform does not support foreign keys.'); + } + + $tableB = new \Doctrine\DBAL\Schema\Table("dbdriver_bar"); + $tableB->createColumn('id', 'integer'); + $tableB->setPrimaryKey(array('id')); + + $sm = $this->_em->getConnection()->getSchemaManager(); + $sm->dropAndCreateTable($tableB); + + $tableA = new \Doctrine\DBAL\Schema\Table("dbdriver_baz"); + $tableA->createColumn('id', 'integer'); + $tableA->setPrimaryKey(array('id')); + $tableA->createColumn('bar_id', 'integer'); + $tableA->addForeignKeyConstraint('dbdriver_bar', array('bar_id'), array('id')); + + $this->_sm = $this->_em->getConnection()->getSchemaManager(); + $this->_sm->dropAndCreateTable($tableA); + + $this->assertClassMetadataYamlEqualsFile(__DIR__."/DatabaseDriver/fkYaml.yml", "DbdriverBaz"); + } +} diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index 814ac22ca..3fce13cfc 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -209,6 +209,10 @@ class OrmFunctionalTestCase extends OrmTestCase protected function onNotSuccessfulTest(\Exception $e) { + if ($e instanceof \PHPUnit_Framework_ExpectationFailedException) { + throw $e; + } + if($this->_sqlLoggerStack->queries !== null && count($this->_sqlLoggerStack->queries)) { $queries = ""; for($i = 0; $i < count($this->_sqlLoggerStack->queries); $i++) {