Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Importing tables from external database in Symfony2 with doctrine

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.

like image 959
Sergi Avatar asked Jul 01 '11 11:07

Sergi


3 Answers

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.

like image 174
jonv1 Avatar answered Oct 08 '22 19:10

jonv1


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.

like image 22
Robin Rizvi Avatar answered Oct 08 '22 20:10

Robin Rizvi


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.

like image 23
sglessard Avatar answered Oct 08 '22 19:10

sglessard