J'ai un projet Symfony2 avec sa propre base de données et je souhaite maintenant me connecter à une autre base de données (un autre projet) afin de pouvoir modifier certaines tables.
J'ai créé la nouvelle connexion dans 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:
J'ai essayé d'importer le schéma avec la commande suivante:
$ php app/console doctrine: mapping: import --em = buv MyBundle yml
[Doctrine\DBAL\Schema\SchemaException] L'index '' n'existe pas dans la table 'old_table'
Mais certaines des tables de la base de données2 n'ont pas de PK! Et l'importation complète ne fonctionne pas. Mais je veux seulement importer deux tables, alors j'ai essayé:
$ php app/console doctrine: mapping: import --em = buv --filter = "nomtable" MyBundle yml
Mais je reçois la même erreur, semble que --filter ne fonctionne pas.
La documentation dans la commande de la console doctrine: mapping: import seulement dit de mettre le nom de l'entité dans l'option de filtrage. Mais je n'ai pas encore d'entité.
Si je vous ai bien compris, vous souhaitez importer votre base de données existante?
Ce que je fais c'est:
php app/console doctrine:mapping:convert xml ./src/App/MyBundle/Resources/config/doctrine/metadata/orm --from-database --force
Puis effectuez une conversion sélective en annotation:
php app/console doctrine:mapping:import AppMyBundle annotation --filter="users_table"
Si vous voulez yml, changez l'annotation en yml.
avertissement: lorsque vous importez dans annotation ou yml, cela supprimera votre fichier d'entité actuel.
Doctrine a l'obligation de d'avoir un identifiant/une clé primaire . Consultez cette page: http://www.doctrine-project.org/docs/orm/ 2.0/fr/reference/basic-mapping.html # identificateurs-clés-primaires
Mais là est un moyen de générer des mappages et des entités à partir de tables qui n’ont pas de clé primaire . Une table sans clé primaire est une conception de base de données inhabituelle et incorrecte, mais un tel scénario existe dans le cas de bases de données héritées.
Solution :
Note : Toutes les références ci-dessous font référence à Doctrine 2.0
1. Trouver le fichier DatabaseDriver.php (dans Doctrine/ORM/Mapping/Driver/DatabaseDriver.php)
2. Trouvez la méthode reverseEngineerMappingFromDatabase . Modifiez le code comme indiqué ci-dessous.
Le code d'origine est:
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;
}
}
}
Le code modifié est:
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. Trouvez la méthode loadMetadataForClass dans le même fichier. Modifiez le code comme indiqué ci-dessous.
Trouvez le code indiqué ci-dessous:
try {
$primaryKeyColumns = $this->tables[$tableName]->getPrimaryKey()->getColumns();
} catch(SchemaException $e) {
$primaryKeyColumns = array();
}
Modifiez-le comme ceci:
try {
$primaryKeyColumns = ($this->tables[$tableName]->hasPrimaryKey())?$this->tables[$tableName]->getPrimaryKey()->getColumns():array();
} catch(SchemaException $e) {
$primaryKeyColumns = array();
}
La solution ci-dessus crée des mappages (xml/yml/annotation) même pour les tables dépourvues de clé primaire.
J'ai réussi à importer des entités de base de données en ajoutant un schema_filter
dans la 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
Puis revenez config.yml.
J'ai créé une solution basée sur tous les commentaires qui simplifie le code
sur la classe espace de noms Doctrine\ORM\Mapping\Driver; DatabaseDriver.php
À la ligne 277, changez:
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();
}
Et, à la ligne 488, ajoutez:
if( $table->hasPrimaryKey() ) //add this if to avoid fatalError
return $table->getPrimaryKey()->getColumns();
Pour éviter tout problème futur, après avoir mappé votre base de données, renvoyez les paramètres pour éviter tout problème ultérieurement. Bonne chance!
Notez que --filter
dans votre commande doit être rempli avec le Entity Class name et non le Table name. Si l'entité n'existe pas encore, le nom de la classe d'entité doit compléter le nom de votre table. Donc, si votre table est user_table
, la valeur du filtre serait UserTable
.
Et pour éviter que votre base de données contienne des tables que Doctrine ne peut pas gérer, vous devez ajouter aux listes que vous souhaitez autoriser la gestion de Doctrine. Vous pouvez le faire dans votre fichier de configuration comme ceci:
doctrine:
dbal:
# ...
schema_filter: /^(users_table|emails)$/
sinon, vous pouvez le spécifier dans votre fichier cli-config.php.
/** @var Doctrine\ORM\Configuration $config */
$config->setFilterSchemaAssetsExpression('/^(users_table|email)$/');
Vous devez mettre à jour la fonction getTablePrimaryKeys pour:
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();
}
Vous pouvez modifier la fonction reverseEngineerMappingFromDatabase du fichier DatabaseDriver.php
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.");
avec
if(! $table->hasColumn('id')){
$table->addColumn('id', 'integer', array('autoincrement' => true));
}
$table->setPrimaryKey(array('id'));
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