可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a Symfony2 project with its own database, and now I want to connect to another database (another project) so I can modify some tables.
I created the new connection in config_dev.yml
doctrine:
dbal:
default_connection: default
connections:
default:
driver: pdo_mysql
host: localhost
dbname: database1
user: root
password:
buv:
driver: pdo_mysql
host: localhost
dbname: database2
user: root
password:
I tried to import the schema with the following command:
$ php app/console doctrine:mapping:import --em=buv MyBundle yml
[Doctrine\DBAL\Schema\SchemaException]
Index '' does not exist on table 'old_table'
But some of the tables in database2 have no PKs! And the full import dosn't work. But I only want to import two tables, so I tried:
$ php app/console doctrine:mapping:import --em=buv --filter="tablename" MyBundle yml
But I'm getting the same error, seems that --filter isn't working.
The documentation in the console command doctrine:mapping:import only says to put the entity name in the filter option. But I don't have an entity yet.
回答1:
If I get you correctly, you want to import your existing database?
What I do is:
php app/console doctrine:mapping:convert xml ./src/App/MyBundle/Resources/config/doctrine/metadata/orm --from-database --force
Then do a selective convert to annotation:
php app/console doctrine:mapping:import AppMyBundle annotation --filter="users_table"
If you wanted to yml, change annotation to yml.
warning: when you import to annotation or yml, it will delete your current entity file.
回答2:
It is a requirement for Doctrine to have an identifier/primary key.
Take a look at this page: http://www.doctrine-project.org/docs/orm/2.0/en/reference/basic-mapping.html#identifiers-primary-keys
But there is a way to generate mappings and entities from tables that do not have a primary key. A table with no primary key is an unusual and bad database design but such a scenario exists in case of legacy databases.
Solution:
Note: All references below refer to Doctrine 2.0
1. Find the file DatabaseDriver.php (in Doctrine/ORM/Mapping/Driver/DatabaseDriver.php)
2. Find the method reverseEngineerMappingFromDatabase. Modify the code as stated below.
The original code is:
private function reverseEngineerMappingFromDatabase()
{
if ($this->tables !== null) {
return;
}
$tables = array();
foreach ($this->_sm->listTableNames() as $tableName) {
$tables[$tableName] = $this->_sm->listTableDetails($tableName);
}
$this->tables = $this->manyToManyTables = $this->classToTableNames = array();
foreach ($tables as $tableName => $table) {
/* @var $table \Doctrine\DBAL\Schema\Table */
if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
$foreignKeys = $table->getForeignKeys();
} else {
$foreignKeys = array();
}
$allForeignKeyColumns = array();
foreach ($foreignKeys as $foreignKey) {
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
}
if ( ! $table->hasPrimaryKey()) {
throw new MappingException(
"Table " . $table->getName() . " has no primary key. Doctrine does not ".
"support reverse engineering from tables that don't have a primary key."
);
}
$pkColumns = $table->getPrimaryKey()->getColumns();
sort($pkColumns);
sort($allForeignKeyColumns);
if ($pkColumns == $allForeignKeyColumns && count($foreignKeys) == 2) {
$this->manyToManyTables[$tableName] = $table;
} else {
// lower-casing is necessary because of Oracle Uppercase Tablenames,
// assumption is lower-case + underscore separated.
$className = $this->getClassNameForTable($tableName);
$this->tables[$tableName] = $table;
$this->classToTableNames[$className] = $tableName;
}
}
}
The modified code is:
private function reverseEngineerMappingFromDatabase()
{
if ($this->tables !== null) {
return;
}
$tables = array();
foreach ($this->_sm->listTableNames() as $tableName) {
$tables[$tableName] = $this->_sm->listTableDetails($tableName);
}
$this->tables = $this->manyToManyTables = $this->classToTableNames = array();
foreach ($tables as $tableName => $table) {
/* @var $table \Doctrine\DBAL\Schema\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=array();
if ($table->hasPrimaryKey()) {
$pkColumns = $table->getPrimaryKey()->getColumns();
sort($pkColumns);
}
sort($allForeignKeyColumns);
if ($pkColumns == $allForeignKeyColumns && count($foreignKeys) == 2) {
$this->manyToManyTables[$tableName] = $table;
} else {
// lower-casing is necessary because of Oracle Uppercase Tablenames,
// assumption is lower-case + underscore separated.
$className = $this->getClassNameForTable($tableName);
$this->tables[$tableName] = $table;
$this->classToTableNames[$className] = $tableName;
}
}
}
3. Find the method loadMetadataForClass in the same file. Modify the code as stated below.
Find the code stated below:
try {
$primaryKeyColumns = $this->tables[$tableName]->getPrimaryKey()->getColumns();
} catch(SchemaException $e) {
$primaryKeyColumns = array();
}
Modify it like this:
try {
$primaryKeyColumns = ($this->tables[$tableName]->hasPrimaryKey())?$this->tables[$tableName]->getPrimaryKey()->getColumns():array();
} catch(SchemaException $e) {
$primaryKeyColumns = array();
}
The above solution creates mappings(xml/yml/annotation) even for tables that don't have a primary key.
回答3:
I've successfully imported some database entities by adding a schema_filter
in the doctrine dbal config (~/app/config/config.yml
)
# Doctrine Configuration
doctrine:
dbal:
driver: %database_driver%
host: %database_host%
port: %database_port%
dbname: %database_name%
user: %database_user%
password: %database_password%
charset: UTF8
schema_filter: /^users_table/
app/console doctrine:mapping:import --force MyBundle yml
Then revert config.yml.
回答4:
I created a solution based on all comments that simplifies the code
on class
namespace Doctrine\ORM\Mapping\Driver;
DatabaseDriver.php
On line 277, change:
if (!$table->hasPrimaryKey()) {
// comment this Throw exception
// throw new MappingException(
// “Table “ . $table->getName() . “ has no primary key.
// Doctrine does not “.
// “support reverse engineering from tables that don’t
// have a primary key.”
// );
} else {
$pkColumns = $table->getPrimaryKey()->getColumns();
}
And, on line 488, add:
if( $table->hasPrimaryKey() ) //add this if to avoid fatalError
return $table->getPrimaryKey()->getColumns();
To avoid any future problems, after mapping your database, return the settings to avoid any problems later.
Good luck!
回答5:
Note that --filter
in your command should be populated with the Entity Class name and not the Table name. If the entity does not yet exists, the Entity Class name must compliment your table name. So if your table is user_table
, the filter value would be UserTable
.
And then to work around that your DB has some tables that Doctrine cannot handle, you should whitelist the tables you do want allow Doctrine to manage. You can do this in your config file like, so:
doctrine:
dbal:
# ...
schema_filter: /^(users_table|emails)$/
alternatively you can specify this in your cli-config.php file.
/** @var Doctrine\ORM\Configuration $config */
$config->setFilterSchemaAssetsExpression('/^(users_table|email)$/');
回答6:
You have to update the getTablePrimaryKeys function to:
private function getTablePrimaryKeys(Table $table)
{
try {
$primaryKeyColumns = ($this->tables[$table->getName()]->hasPrimaryKey())?$this->tables[$table->getName()]->getPrimaryKey()->getColumns():array();
} catch(SchemaException $e) {
$primaryKeyColumns = array();
}
return array();
}
回答7:
php bin/console doctrine:mapping:convert xml ./src/NameBundle/Resources/doctrine/metadata/orm
php bin/console doctrine:mapping:import NameBundle yml
php bin/console doctrine:generate:entities NameBundle
回答8:
At the DatabaseDriver.php file reverseEngineerMappingFromDatabase function you can change
throw new MappingException("Table " . $table->getName() . " has no primary key. Doctrine does not "."support reverse engineering from tables that don't have a primary key.");
with
if(! $table->hasColumn('id')){
$table->addColumn('id', 'integer', array('autoincrement' => true));
}
$table->setPrimaryKey(array('id'));