Search code examples
phpsymfonydoctrine-ormdoctrine-odmdoctrine-mongodb

Is it possible to use two diffrent document managers with different databases for the same bundle in Symfony?


My goal is to have two different document managers connected to different databases that share the same database models.

I have heavily changed my database model and I would like to write a custom migration script that retrieves objects from the old model reads its values and then creates a new object on the new schema with the information of the old object.

I have found a related stackoverflow question here: Working with two entity managers in the same bundle in Symfony2

Howewer, this solution suggests to use different prefixes for each database and it stores the classes for the data model in different folders:

doctrine:
dbal:
    default_connection:   default
    connections:
        default:
            driver:   %database_driver%
            host:     %database_host%
            port:     %database_port%
            dbname:   %database_name%
            user:     %database_user%
            password: %database_password%
            charset:  UTF8
        second:
            driver:   %database_sqlite_driver%
            host:     ~
            port:     ~
            dbname:   %database_sqlite_shop_name%
            path:     %database_sqlite_shop_name%
            user:     ~
            password: ~
            charset:  UTF8

orm:
    auto_generate_proxy_classes: %kernel.debug%
    default_entity_manager:   default
    entity_managers:
        default:
            connection:       default
            mappings:
                YourBundle:
                  # you must specify the type
                  type:     "annotation"    
                  # The directory for entity (relative to bundle path)
                  dir:      "Entity/FirstDb"        
                  #the prefix 
                  prefix:   "Your\Bundle\Entity\FirstDb" 
        shop:
            connection:       second
            mappings:
                YourBundle:
                  type: "annotation"
                  #here the second path where entity for the connection stand
                  dir: "Entity/SecondDb" 
                  #the prefix
                  prefix: "Your\Bundle\Entity\SecondDb" 

I would really just like to have two different document manager objects that share the same models in the same folder but are connected to diffrent databases. Is this possible?


Solution

  • I found out that it is indeed possible to be connected to different databases in the same Symfony Bundle. This answer led me to a possible solution: https://stackoverflow.com/a/15110867/2174832

    #services.yml
    acme_app.dynamic_connection:
    class: %acme.dynamic_doctrine_connection.class%
    calls:
        - [setDoctrineConnection, @doctrine.dbal.default_connection]]
    
    
    <?php
    
    namespace Acme\Bundle\AppBundle;
    
    use Doctrine\DBAL\Connection;
    use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
    use Exception;
    
    class DynamicDoctrineConnection
    {
        /**
         * @var Connection
         */
        private $connection;
    
        /**
         * Sets the DB Name prefix to use when selecting the database to connect to
         *
         * @param  Connection       $connection
         * @return SiteDbConnection $this
         */
        public function setDoctrineConnection(Connection $connection)
        {
            $this->connection = $connection;
    
            return $this;
        }
    
        public function setUpAppConnection()
        {
            if ($this->request->attributes->has('appId')) {
                $connection = $this->connection;
                $params     = $this->connection->getParams();
    
                // we also check if the current connection needs to be closed based on various things
                // have left that part in for information here
                // $appId changed from that in the connection?
                // if ($connection->isConnected()) {
                //     $connection->close();
                // }
    
                // Set default DB connection using appId
                //$params['host']   = $someHost;
                $params['dbname'] = 'Acme_App'.$this->request->attributes->get('appId');
    
                // Set up the parameters for the parent
                $connection->__construct(
                    $params, $connection->getDriver(), $connection->getConfiguration(),
                    $connection->getEventManager()
                );
    
                try {
                    $connection->connect();
                } catch (Exception $e) {
                    // log and handle exception
                }
            }
    
            return $this;
        }
    }
    

    With the solution above one can write a service that can be called to change the database that the current entity manager is connected to to a different database.

    Unfortunately the solution above only works for the PDO driver (Mysql). Since our technology stack includes mongodb and we use the doctrine-mongodb bundle I had to look for a differnt solution.

    The doctrine-mongodb documentation has a section about setting up a custom document manager here: http://docs.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/introduction.html

    I was unable to get the right doctrine-mongodb odm document mappings to work with the instructions in the doc and therefore the only solution for me was to create a simple PHP mongoclient connection:

    $mongo = new \Mongo('mongodb://localhost:27017');
    $legacyDbDocumentMangager = $mongo->selectDB('backed_up_prod_db');
    $legacyUserCollection = $legacyDbDocumentMangager->selectCollection('User');
    $user = $legacyUserCollection->findOne(array('email' => '[email protected]'));
    

    The only differnce between this simple php mongodb driver and the doctrine-mongodb odm is that the results of the queries of this driver are associative arrays and the results of the doctrine-mongodb odm diver are objects.

    I hope this information is useful to someone.