web-dev-qa-db-fra.com

Importation de tables à partir d'une base de données externe dans Symfony2 avec doctrine

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é.

23
Sergi

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.

14
jonv1

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.

15
Robin Rizvi

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.

4
sglessard

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!

3
João Neto

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)$/');
3
Courtney Miles

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();
}
1
bstricks

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'));
0
Sofien Benrhouma
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
0
Tarek Khalfaoui