Symfony 3 - EntityManager dependency injection with multiple db connections
Asked Answered
L

3

6

I have setup a Custom Authenticator using guard and auto wired the service. This is tested and works fine with just MySQL configured.

I have now specified a second database connection (oracle), but Symfony will now not allow autowiring in my service configuration, because it does not know which database connection to use when injecting EntityManager in to the custom Authenticator class.

Any idea how I can Configure the Dependency Injection to use a specific database connection so I can continue to use AutoWire.

Unable to autowire argument of type "Doctrine\ORM\EntityManager" for the service "user.security.login_form_authenticator". Multiple services exist for this class (doctrine.orm.prism_entity_manager, doctrine.orm.baan_entity_manager).

Here is my Doctrine config in config.yml

doctrine:
    dbal:
        connections:
            prism:
                driver:   pdo_mysql
                host:     "%database_host%"
                port:     "%database_port%"
                dbname:   "%database_name%"
                user:     "%database_user%"
                password: "%database_password%"
                charset:  UTF8
                # if using pdo_sqlite as your database driver:
                #   1. add the path in parameters.yml
                #     e.g. database_path: "%kernel.root_dir%/../var/data/data.sqlite"
                #   2. Uncomment database_path in parameters.yml.dist
                #   3. Uncomment next line:
                #path:     "%database_path%"
            baan:
                driver:   oci8
                host:     "%baan_host%"
                port:     "%baan_port%"
                dbname:   "%baan_db_name%"
                user:     "%baan_user%"
                password: "%baan_password%"
                charset:  AL32UTF8

    orm:
        default_entity_manager: prism
        auto_generate_proxy_classes: "%kernel.debug%"
        entity_managers:
            auto_mapping: true
            prism:
                naming_strategy: doctrine.orm.naming_strategy.underscore
                connection: prism
                mappings:
                    UserBundle:
                        type: annotation

            baan:
                connection: baan
                mappings:
                    BaanBundle:
                        type: annotation

Here is the constructor in my Authenticator class

 private $formFactory;

    private $em;

    private $router;


    public function __construct(FormFactoryInterface $formFactory, EntityManager $em, RouterInterface $router)
    {
        $this->formFactory = $formFactory;
        $this->em = $em;
        $this->router = $router;
    }
Laughable answered 27/3, 2017 at 8:49 Comment(1)
Easy. Don't use autowire. I think there may actually be a work around (check the docs) but why add even more magic.Clarsach
C
12

You can extend Doctrine's EntityManagerDecorator, which implements EntityManagerInterface and accepts an instance of EntityManager in its constructor.

First extend the EntityManagerDecorator class once for each of your connections.

namespace MyBundle\Service\Database;

use Doctrine\ORM\Decorator\EntityManagerDecorator;

class PrismEntityManager extends EntityManagerDecorator {}

class BaanEntityManager extends EntityManagerDecorator {}

Then in your service configuration, you need to manually wire these two services.

MyBundle\Service\Database\PrismEntityManager:
    arguments:
        $wrapped: '@doctrine.orm.prism_entity_manager'

MyBundle\Service\Database\BaanEntityManager:
    arguments:
        $wrapped: '@doctrine.orm.baan_entity_manager'

Now you just have to type-hint for one of these services.

public function __construct(FormFactoryInterface $formFactory, PrismEntityManager $em, RouterInterface $router)
{
    $this->formFactory = $formFactory;
    $this->em = $em;
    $this->router = $router;
}
Chavis answered 14/8, 2017 at 18:6 Comment(0)
G
2

I don't know if i understood your question correctly or not but you can set different configurations for different database connection as below:

dbal:
    default_connection: default
    connections:
        default:
            driver:   pdo_mysql
            host:     "%database_host%"
            port:     "%database_port%"
            dbname:   "%database_name%"
            user:     "%database_user%"
            password: "%database_password%"
            charset:  UTF8
            mapping_types:
            enum: smallint
        custom:
            driver:   pdo_mysql
            host:     '%database_host2%'
            port:     '%database_port2%'
            dbname:   '%database_name2%'
            user:     '%database_user2%'
            password: '%database_password2%'
            charset:  UTF8
            mapping_types:
            enum: smallint
    orm:
        default_entity_manager: default
        auto_generate_proxy_classes: "%kernel.debug%"
        entity_managers:
            auto_mapping: true
            default:
                naming_strategy: doctrine.orm.naming_strategy.underscore
                connection: default
                mappings:
                    EntityBundle:
                        type: annotation
                        alias: DBAlias
            custom:
                naming_strategy: doctrine.orm.naming_strategy.underscore
                connection: custom
                mappings:
                    EntityBundle:
                        type: annotation
                        alias: DBAlias

Now you can passed your custom EntityManager using doctrine.orm.custom_entity_manager.

Globuliferous answered 27/3, 2017 at 9:13 Comment(5)
I have added my doctrine config and class constructor to the question. How do I pass the "prism" configuration to $em in my class constructor?Laughable
You can register your class into the service and then passed @doctrine.orm.prism_entity_manager as argument.Globuliferous
Just let me know if it is working for you or not? thanks in advance :)Globuliferous
I have three arguments in my constructor. Can I use a mixture of autowire and passed arguments, or do I have to only do one or the other?Laughable
I think you need to define the arguments from where you register your class, Because if there are multiple same class like in your case then you need to define them using arguments instead of autowire, Please check symfony.com/doc/current/components/dependency_injection/… for more details. and also check knpuniversity.com/screencast/new-in-symfony3/autowiringGlobuliferous
R
0

I think, I had the same issue with DBAL Connections as you have with EntityManager. I have solved this issue with some proxy class - described in this answer: https://mcmap.net/q/1405641/-autowire-specific-dbal-connection-when-using-multiple-of-them

Any question let me know.

Rossner answered 2/10, 2017 at 20:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.