4

I would like to use the autowiring in a service that use 2 different entity manager. How to achieve something like that ?

use Doctrine\ORM\EntityManager;
class TestService
{
    public function __construct(EntityManager $emA, EntityManager $emB)
    {
    }
}

My service.yml file use to be configured like that :

    app.testservice:
        class: App\Services\TestService
        arguments:
            - "@doctrine.orm.default_entity_manager"
            - "@doctrine.orm.secondary_entity_manager"
Vivien
  • 1,159
  • 2
  • 14
  • 34

4 Answers4

5

There are already two good answers posted but I'd like to add a third as well as some context to help chose which approach to use in a given situation.

emix's answer is very simple but a bit fragile in that it relies on the name of the argument for injecting the correct service. Which is fine but you won't get any help from your IDE and sometimes might be a bit awkward. The answer should probably use EntityManagerInterface but that is a minor point.

DynlanKas's answer requires a bit of code in each service to locate the desired manager. It's okay but can be a bit repetitive. On the other hand, the answer is perfect when you don't know in advance exactly which manager is needed. It allows you to select a manager based on some dynamic information.

This third answer is largely based on Ron's Answer but refined just a bit.

Make a new class for each entity manager:

namespace App\EntityManager;
use Doctrine\ORM\Decorator\EntityManagerDecorator;
class AEntityManager extends EntityManagerDecorator {}
class BEntityManager extends EntityManagerDecorator {}

Don't be alarmed that you are extending a decorator class. The class has the same interface and the same functionality as a 'real' entity manager. You just need to inject the desired manager:

# config/services.yaml
App\EntityManager\AEntityManager:
    decorates: doctrine.orm.a_entity_manager

App\EntityManager\BEntityManager:
    decorates: doctrine.orm.b_entity_manager

This approach requires making a new class for each entity manager as well as a couple of lines of configuration, but allows you to simply typehint against the desired class:

public function __construct(AEntityManager $emA, BEntityManager $emB)
{
}

It is, arguably, the most robust and standard way to approach the original question.

Cerad
  • 48,157
  • 8
  • 90
  • 92
  • In case the configuration is broken / you rename parameters in the constructor, the cache won't compile. The `cache:clear` will fail saying it doesn't know how to wire the service so I wouldn't call it "fragile". – Mike Doe Dec 12 '19 at 14:10
  • I didn't have the renaming issue. This is my preferred way since it can be typehinted. – Pierre de LESPINAY Mar 05 '20 at 08:29
2

Dylan's answer violates the Demeter's Law principle. It's very easy and elegant since Symfony 3.4, meet Local service binding:

services:
  _defaults:
    bind:
      $emA: "@doctrine.orm.default_entity_manager"
      $emB: "@doctrine.orm.secondary_entity_manager"

Then in your service the autoloading will do the hard work for you:

class TestService
{
    public function __construct(EntityManager $emA, EntityManager $emB)
    {
        …
    }
}
Mike Doe
  • 16,349
  • 11
  • 65
  • 88
1

The easy way would be to autowire ManagerRegistry in your constructor and use it to get the managers you want by using the names of the entity manger you have set in your configuration file (doctrine.yaml) :

use Doctrine\Common\Persistence\ManagerRegistry;
class TestService
{

    private $emA;
    private $emB;
    public function __construct(ManagerRegistry $doctrine)
    {
         $this->emA = $doctrine->getManager('emA');
         $this->emB = $doctrine->getManager('emB');
    }
}

And you should be able to use them as you want.


Another way would be to follow this answer by Ron Mikluscak

Dylan KAS
  • 4,840
  • 2
  • 15
  • 33
  • Your copy/paste of Ron's answer is not quite right. – Cerad Dec 10 '19 at 13:32
  • It may be, i'm not very experienced with decorators on symfony, feel free to explain where is the problem if you want me to try and modify the answer – Dylan KAS Dec 10 '19 at 13:50
  • No thanks. I guess I am a bit old school. I think people posting answers have the responsibility to make sure they post the correct information. Consider making a simple test case or at the very least, read Ron's answer. – Cerad Dec 10 '19 at 13:55
  • Well I did test my answer and I felt it would be nice to point out Ron's answer, but I guess you are right and I should just link it instead. Will update – Dylan KAS Dec 10 '19 at 13:56
  • Kind of shame. While your answer does work, Ron's answer is the better approach. – Cerad Dec 10 '19 at 14:16
0

Simply use EntityManagerInterface $secondaryEntityManager

If you're using Symfony's framework bundle (which I'm pretty sure you are), then Symfony >= 4.4 automatically generates camelcased autowiring aliases for every Entitymanger you define.

You can simply get a list of them using the debug:autowiring console command. For your configuration above, this should look something like this:

bin/console debug:autowiring EntityManagerInterface

Autowirable Types
=================

 The following classes & interfaces can be used as type-hints when autowiring:
 (only showing classes/interfaces matching EntityManagerInterface)

 EntityManager interface
 Doctrine\ORM\EntityManagerInterface (doctrine.orm.default_entity_manager)
 Doctrine\ORM\EntityManagerInterface $defaultEntityManager (doctrine.orm.default_entity_manager)
 Doctrine\ORM\EntityManagerInterface $secondaryEntityManager (doctrine.orm.secondary_entity_manager)

As described in https://symfony.com/doc/4.4/doctrine/multiple_entity_managers.html:

Entity managers also benefit from autowiring aliases when the framework bundle is used. For example, to inject the customer entity manager, type-hint your method with EntityManagerInterface $customerEntityManager.

So you should only need:

use Doctrine\ORM\EntityManagerInterface;

class TestService
{
  public function __construct(
    EntityManagerInterface $defaultEntityManager,
    EntityManagerInterface $secondaryEntityManager
  ) {
    // ...
  }
}

The name $defaultEntityManager isn't mandatory, though it helps to distinguish between the two. Every argument that's typehinted with an EntityManagerInterface and isn't in the list returned by debug:autowiring EntityManagerInterface will result in the default Entitymanager being autowired.

Note: As written in the documentation and shown in the output of debug:autowiring, you need to use EntityManagerInterface for this aliased autowiring, not the actual EntityManager class. In fact, you should always autowire the Entitymanager using EntityManagerInterface.

flu
  • 14,307
  • 8
  • 74
  • 71