Merge branch 'DDC-616'
This commit is contained in:
commit
206710f91a
@ -38,8 +38,20 @@ use Doctrine\Common\Cache\ArrayCache,
|
|||||||
*/
|
*/
|
||||||
class DatabaseDriver implements Driver
|
class DatabaseDriver implements Driver
|
||||||
{
|
{
|
||||||
/** The SchemaManager. */
|
/**
|
||||||
|
* @var AbstractSchemaManager
|
||||||
|
*/
|
||||||
private $_sm;
|
private $_sm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $tables = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $manyToManyTables = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
|
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
|
||||||
@ -51,22 +63,69 @@ class DatabaseDriver implements Driver
|
|||||||
{
|
{
|
||||||
$this->_sm = $schemaManager;
|
$this->_sm = $schemaManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function reverseEngineerMappingFromDatabase()
|
||||||
|
{
|
||||||
|
if ($this->tables !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->_sm->listTableNames() as $tableName) {
|
||||||
|
$tableName = strtolower($tableName);
|
||||||
|
$tables[$tableName] = $this->_sm->listTableDetails($tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->tables = array();
|
||||||
|
foreach ($tables AS $name => $table) {
|
||||||
|
/* @var $table Table */
|
||||||
|
if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
|
||||||
|
$foreignKeys = $table->getForeignKeys();
|
||||||
|
} else {
|
||||||
|
$foreignKeys = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$allForeignKeyColumns = array();
|
||||||
|
foreach ($foreignKeys AS $foreignKey) {
|
||||||
|
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
|
||||||
|
}
|
||||||
|
|
||||||
|
$pkColumns = $table->getPrimaryKey()->getColumns();
|
||||||
|
sort($pkColumns);
|
||||||
|
sort($allForeignKeyColumns);
|
||||||
|
|
||||||
|
if ($pkColumns == $allForeignKeyColumns) {
|
||||||
|
if (count($table->getForeignKeys()) != 2) {
|
||||||
|
throw new \InvalidArgumentException("ManyToMany tables with more or less than two foreign keys are not supported by the Database Reverese Engineering Driver.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->manyToManyTables[$name] = $table;
|
||||||
|
} else {
|
||||||
|
$this->tables[$name] = $table;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function loadMetadataForClass($className, ClassMetadataInfo $metadata)
|
public function loadMetadataForClass($className, ClassMetadataInfo $metadata)
|
||||||
{
|
{
|
||||||
$tableName = $className;
|
$this->reverseEngineerMappingFromDatabase();
|
||||||
$className = Inflector::classify(strtolower($tableName));
|
|
||||||
|
$tableName = Inflector::tableize($className);
|
||||||
|
|
||||||
$metadata->name = $className;
|
$metadata->name = $className;
|
||||||
$metadata->table['name'] = $tableName;
|
$metadata->table['name'] = $tableName;
|
||||||
|
|
||||||
$columns = $this->_sm->listTableColumns($tableName);
|
if (!isset($this->tables[$tableName])) {
|
||||||
|
throw new \InvalidArgumentException("Unknown table " . $tableName . " referred from class " . $className);
|
||||||
|
}
|
||||||
|
|
||||||
|
$columns = $this->tables[$tableName]->getColumns();
|
||||||
|
$indexes = $this->tables[$tableName]->getIndexes();
|
||||||
|
|
||||||
if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
|
if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
|
||||||
$foreignKeys = $this->_sm->listTableForeignKeys($tableName);
|
$foreignKeys = $this->tables[$tableName]->getForeignKeys();
|
||||||
} else {
|
} else {
|
||||||
$foreignKeys = array();
|
$foreignKeys = array();
|
||||||
}
|
}
|
||||||
@ -76,8 +135,6 @@ class DatabaseDriver implements Driver
|
|||||||
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
|
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
|
||||||
}
|
}
|
||||||
|
|
||||||
$indexes = $this->_sm->listTableIndexes($tableName);
|
|
||||||
|
|
||||||
$ids = array();
|
$ids = array();
|
||||||
$fieldMappings = array();
|
$fieldMappings = array();
|
||||||
foreach ($columns as $column) {
|
foreach ($columns as $column) {
|
||||||
@ -121,15 +178,64 @@ class DatabaseDriver implements Driver
|
|||||||
$metadata->mapField($fieldMapping);
|
$metadata->mapField($fieldMapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($foreignKeys as $foreignKey) {
|
foreach ($this->manyToManyTables AS $manyTable) {
|
||||||
$cols = $foreignKey->getColumns();
|
foreach ($manyTable->getForeignKeys() AS $foreignKey) {
|
||||||
$localColumn = current($cols);
|
if ($tableName == strtolower($foreignKey->getForeignTableName())) {
|
||||||
|
$myFk = $foreignKey;
|
||||||
|
foreach ($manyTable->getForeignKeys() AS $foreignKey) {
|
||||||
|
if ($foreignKey != $myFk) {
|
||||||
|
$otherFk = $foreignKey;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$localColumn = current($myFk->getColumns());
|
||||||
|
$associationMapping = array();
|
||||||
|
$associationMapping['fieldName'] = Inflector::camelize(str_replace('_id', '', strtolower(current($otherFk->getColumns()))));
|
||||||
|
$associationMapping['targetEntity'] = Inflector::classify(strtolower($otherFk->getForeignTableName()));
|
||||||
|
if (current($manyTable->getColumns())->getName() == $localColumn) {
|
||||||
|
$associationMapping['inversedBy'] = Inflector::camelize(str_replace('_id', '', strtolower(current($myFk->getColumns()))));
|
||||||
|
$associationMapping['joinTable'] = array(
|
||||||
|
'name' => strtolower($manyTable->getName()),
|
||||||
|
'joinColumns' => array(),
|
||||||
|
'inverseJoinColumns' => array(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$fkCols = $myFk->getForeignColumns();
|
||||||
|
$cols = $myFk->getColumns();
|
||||||
|
for ($i = 0; $i < count($cols); $i++) {
|
||||||
|
$associationMapping['joinTable']['joinColumns'][] = array(
|
||||||
|
'name' => $cols[$i],
|
||||||
|
'referencedColumnName' => $fkCols[$i],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fkCols = $otherFk->getForeignColumns();
|
||||||
|
$cols = $otherFk->getColumns();
|
||||||
|
for ($i = 0; $i < count($cols); $i++) {
|
||||||
|
$associationMapping['joinTable']['inverseJoinColumns'][] = array(
|
||||||
|
'name' => $cols[$i],
|
||||||
|
'referencedColumnName' => $fkCols[$i],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$associationMapping['mappedBy'] = Inflector::camelize(str_replace('_id', '', strtolower(current($myFk->getColumns()))));
|
||||||
|
}
|
||||||
|
$metadata->mapManyToMany($associationMapping);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($foreignKeys as $foreignKey) {
|
||||||
|
$foreignTable = $foreignKey->getForeignTableName();
|
||||||
|
$cols = $foreignKey->getColumns();
|
||||||
$fkCols = $foreignKey->getForeignColumns();
|
$fkCols = $foreignKey->getForeignColumns();
|
||||||
|
|
||||||
|
$localColumn = current($cols);
|
||||||
$associationMapping = array();
|
$associationMapping = array();
|
||||||
$associationMapping['fieldName'] = Inflector::camelize(str_replace('_id', '', strtolower($localColumn)));
|
$associationMapping['fieldName'] = Inflector::camelize(str_replace('_id', '', strtolower($localColumn)));
|
||||||
$associationMapping['targetEntity'] = Inflector::classify($foreignKey->getForeignTableName());
|
$associationMapping['targetEntity'] = Inflector::classify($foreignTable);
|
||||||
|
|
||||||
for ($i = 0; $i < count($cols); $i++) {
|
for ($i = 0; $i < count($cols); $i++) {
|
||||||
$associationMapping['joinColumns'][] = array(
|
$associationMapping['joinColumns'][] = array(
|
||||||
@ -137,7 +243,6 @@ class DatabaseDriver implements Driver
|
|||||||
'referencedColumnName' => $fkCols[$i],
|
'referencedColumnName' => $fkCols[$i],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$metadata->mapManyToOne($associationMapping);
|
$metadata->mapManyToOne($associationMapping);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,19 +258,14 @@ class DatabaseDriver implements Driver
|
|||||||
/**
|
/**
|
||||||
* Return all the class names supported by this driver.
|
* Return all the class names supported by this driver.
|
||||||
*
|
*
|
||||||
* IMPORTANT: This method must return an array of table names because we need
|
* IMPORTANT: This method must return an array of class not tables names.
|
||||||
* to know the table name after we inflect it to create the entity class name.
|
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getAllClassNames()
|
public function getAllClassNames()
|
||||||
{
|
{
|
||||||
$classes = array();
|
$this->reverseEngineerMappingFromDatabase();
|
||||||
|
|
||||||
foreach ($this->_sm->listTableNames() as $tableName) {
|
|
||||||
$classes[] = $tableName;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $classes;
|
return array_map(array('Doctrine\Common\Util\Inflector', 'classify'), array_keys($this->tables));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,6 +16,7 @@ class DatabaseDriverTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
|||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
|
$this->useModelSet('cms');
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
$this->_sm = $this->_em->getConnection()->getSchemaManager();
|
$this->_sm = $this->_em->getConnection()->getSchemaManager();
|
||||||
@ -36,8 +37,8 @@ class DatabaseDriverTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
|||||||
|
|
||||||
$metadatas = $this->extractClassMetadata(array("DbdriverFoo"));
|
$metadatas = $this->extractClassMetadata(array("DbdriverFoo"));
|
||||||
|
|
||||||
$this->assertArrayHasKey('dbdriver_foo', $metadatas);
|
$this->assertArrayHasKey('DbdriverFoo', $metadatas);
|
||||||
$metadata = $metadatas['dbdriver_foo'];
|
$metadata = $metadatas['DbdriverFoo'];
|
||||||
|
|
||||||
$this->assertArrayHasKey('id', $metadata->fieldMappings);
|
$this->assertArrayHasKey('id', $metadata->fieldMappings);
|
||||||
$this->assertEquals('id', $metadata->fieldMappings['id']['fieldName']);
|
$this->assertEquals('id', $metadata->fieldMappings['id']['fieldName']);
|
||||||
@ -74,8 +75,8 @@ class DatabaseDriverTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
|||||||
|
|
||||||
$metadatas = $this->extractClassMetadata(array("DbdriverBar", "DbdriverBaz"));
|
$metadatas = $this->extractClassMetadata(array("DbdriverBar", "DbdriverBaz"));
|
||||||
|
|
||||||
$this->assertArrayHasKey('dbdriver_baz', $metadatas);
|
$this->assertArrayHasKey('DbdriverBaz', $metadatas);
|
||||||
$bazMetadata = $metadatas['dbdriver_baz'];
|
$bazMetadata = $metadatas['DbdriverBaz'];
|
||||||
|
|
||||||
$this->assertArrayNotHasKey('barId', $bazMetadata->fieldMappings, "The foreign Key field should not be inflected as 'barId' field, its an association.");
|
$this->assertArrayNotHasKey('barId', $bazMetadata->fieldMappings, "The foreign Key field should not be inflected as 'barId' field, its an association.");
|
||||||
$this->assertArrayHasKey('id', $bazMetadata->fieldMappings);
|
$this->assertArrayHasKey('id', $bazMetadata->fieldMappings);
|
||||||
@ -86,6 +87,24 @@ class DatabaseDriverTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
|||||||
$this->assertType('Doctrine\ORM\Mapping\OneToOneMapping', $bazMetadata->associationMappings['bar']);
|
$this->assertType('Doctrine\ORM\Mapping\OneToOneMapping', $bazMetadata->associationMappings['bar']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testDetectManyToManyTables()
|
||||||
|
{
|
||||||
|
if (!$this->_em->getConnection()->getDatabasePlatform()->supportsForeignKeyConstraints()) {
|
||||||
|
$this->markTestSkipped('Platform does not support foreign keys.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$metadatas = $this->extractClassMetadata(array("CmsUsers", "CmsGroups"));
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('CmsUsers', $metadatas, 'CmsUsers entity was not detected.');
|
||||||
|
$this->assertArrayHasKey('CmsGroups', $metadatas, 'CmsGroups entity was not detected.');
|
||||||
|
|
||||||
|
$this->assertEquals(1, count($metadatas['CmsUsers']->associationMappings));
|
||||||
|
$this->assertArrayHasKey('group', $metadatas['CmsUsers']->associationMappings);
|
||||||
|
$this->assertEquals(1, count($metadatas['CmsGroups']->associationMappings));
|
||||||
|
$this->assertArrayHasKey('user', $metadatas['CmsGroups']->associationMappings);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param string $className
|
* @param string $className
|
||||||
@ -97,13 +116,13 @@ class DatabaseDriverTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
|||||||
$metadatas = array();
|
$metadatas = array();
|
||||||
|
|
||||||
$driver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver($this->_sm);
|
$driver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver($this->_sm);
|
||||||
foreach ($driver->getAllClassNames() as $dbTableName) {
|
foreach ($driver->getAllClassNames() as $className) {
|
||||||
if (!in_array(strtolower(Inflector::classify($dbTableName)), $classNames)) {
|
if (!in_array(strtolower($className), $classNames)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$class = new ClassMetadataInfo($dbTableName);
|
$class = new ClassMetadataInfo($className);
|
||||||
$driver->loadMetadataForClass($dbTableName, $class);
|
$driver->loadMetadataForClass($className, $class);
|
||||||
$metadatas[strtolower($dbTableName)] = $class;
|
$metadatas[$className] = $class;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count($metadatas) != count($classNames)) {
|
if (count($metadatas) != count($classNames)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user