Autowire specific DBAL connection when using multiple of them

6.1k views Asked by At

I'm using Doctrine 2 where I have multiple connections for DBAL. I have also multiple EntityManagers in ORM.

I need to be able to somehow autowire specific DBAL connection into other Symfony 3 services.

I can autowire any EntitiyManager using EntityManagerDecorator but don't know how to do the same with connection. I'm able to get the connection from EntityManager but I don't think it's the way to go.

5

There are 5 answers

3
Guzik On BEST ANSWER

You can specify wrapper class for doctrine connections, no proxy class needed:

#config.yml
doctrine:
    dbal:
        connections:
            default:
                wrapper_class: AppBundle\Connections\ConnectionDefault
                ...
            second:
                wrapper_class: AppBundle\Connections\ConnectionSecond
                ...

Connections should extend Doctrine\DBAL\Connection:

<?php

namespace AppBundle\Connection;

use Doctrine\DBAL\Connection;

class ConnectionDefault extends Connection
{

}

class ConnectionSecond extends Connection
{

}

and create service aliases:

#services.yml
services:
    ...
    AppBundle\Connections\ConnectionDefault: '@doctrine.dbal.default_connection'
    AppBundle\Connections\ConnectionSecond: '@doctrine.dbal.second_connection'

then you can simply inject desired connection into service by typehinting it:

class MyService {
    public function __construct(ConnectionDefault $connection) {...}
}

class MyOtherService {
    public function __construct(ConnectionSecond $connection) {...}
}
0
Michał Szczech On

Each DBAL connection is always accessible in service container with the following identifier:

doctrine.dbal.[name]_connection

where [name] is the connection name

https://github.com/doctrine/DoctrineBundle/blob/master/Resources/doc/configuration.rst#doctrine-dbal-configuration

2
Arkowsky On

I had the same issue and tested it more precisely what works and why.

WORK FOR MANY DBAL CONNECTIONS USING PROXY

EDIT: I have done some proxy abstract class. Now each my connection class inherited from this proxy. It works fine :)

<?php

#src/AppBundle/Connections/ConnectionProxy.php

namespace AppBundle\Connections;

use Doctrine\DBAL\Connection;

abstract class ConnectionProxy
{
    private $connection;

    public function __construct(Connection $connection)
    {
        $this->connection = $connection;
    }

    public function __call($method, $arguments)
    {
        if (is_callable(array($this->connection, $method))) {
            return call_user_func_array(array($this->connection, $method), $arguments);
        } else {
            return new \Exception("Call to undefined method '{$method}'");
        }
    }
}

My connection default class:

<?php

#src/AppBundle/Connections/ConnectionDefault.php

namespace AppBundle\Connections;

class ConnectionDefault extends ConnectionProxy
{

}

second connection:

<?php

namespace AppBundle\Connections;

class ConnectionSecond extends ConnectionProxy
{

}

Then I have added manually to my services file, for all my connections(2 connections) :

AppBundle\Connections\ConnectionDefault:
    public: true
    class: AppBundle\Connections\ConnectionDefault
    arguments: ['@doctrine.dbal.default_connection']

AppBundle\Connections\ConnectionSecond:
    public: true
    class: AppBundle\Connections\ConnectionSecond
    arguments: ['@doctrine.dbal.second_connection']

Then my connections are automatically autowiring when I use their types

class SaveEvent
{
    /** @var  Connection */
    private $connection;

    public function __construct(ConnectionDefault $connection)
    {
        $this->connection = $connection;
    }
    .....

INJECT CONNECTIONS FOR EACH SERVICE

The simplest option - but require create each service separately and inject connection name - it means manually wiring Arguments** in your services file(app/config/services.yml) e.g.

AppBundle\Classes\SaveEvent:
    public: true
    arguments:
      - '@doctrine.dbal.[connection_name]_connection'

where connection_name is your connection name. In below example we have two connections in config.yml and this value could be “default” or “database2”:

doctrine:
    dbal:
        default_connection: default
        connections:
            default:
              driver: pdo_sqlite
              charset: UTF8
              path: '%database_path%'
            database2:
              driver: pdo_sqlite
              charset: UTF8
              path: '%database_path%'

WORK FOR ONLY ONE DBAL CONNECTION

Autowiring for DBAL Connections works fine when we have only one connection. If I leave only default connection(remove connection database 2 from config.yml) and add some path to be autowired in app/config/services.yml:

AppBundle\Classes\:
    resource: '../../src/AppBundle/Classes'
    public: true

and if I use as type class Doctrine\DBAL\Connection in my Class, it means that I want inject default connection(@doctrine.dbal.default_connection) because I have the only one connection.

namespace AppBundle\Classes;

use Doctrine\DBAL\Connection;

class SaveEvent
{
    /** @var  Connection */
    private $connection;

    public function __construct(Connection $connection)
    {
        $this->connection = $connection;
    }

// …
}

ANSWER

// Edited SimPod, the answer to your question is WORK FOR MANY DBAL CONNECTIONS USING PROXY or INJECT CONNECTIONS FOR EACH SERVICE as I described above.

I'm aware that you have managed with that issue but my answer can help the others.

1
Emanuel Milani On

A simpler solution can be:

#services.yml
services:
    _defaults:
        bind:
            $dbSecond: '@doctrine.dbal.second_connection'

Use in controller or services:

public function my(Request $request, Connection $dbSecond)
3
123pierre On

there is a new functionality in version 3.4 which gives the process much easier. See : Symfony 3.3 - Entity Manager injection into services with multiple database?