Inject the ManagerRegistry instead of the EntityManager

Posted on by Matthias Noback

Please also read my next article about injecting entity managers.

You probably know that you should trim your controllers as much as possible. This usually means extracting some classes from the semi-procedural code inside your controller class. You create services for the extracted classes and inject the necessary dependencies into them. You can no longer use the convenient $this->getDoctrine()->getManager(). Instead you inject the doctrine.orm.default_entity_manager service into every service that needs the EntityManager to persist data in your relational database:

namespace AcmeCustomerBundle\Entity;

use Doctrine\ORM\EntityManager;

class CustomerManager
{
    private $entityManager;

    public function __construct(EntityManager $entityManager)
    {
        $this->entityManager = $entityManager;
    }

    public function createCustomer(Customer $customer)
    {
        $this->entityManager->persist($customer);
        $this->entityManager->flush();
    }
}

When you don't intend to reuse this code in other projects, or share it as open source software: no problem! But when you do, consider the following. The DoctrineBundle offers a way to set up multiple entity managers and database connections. This way you can persist different types of entities in your project in different database. Though I guess most projects use just one entity manager and one database connection, when you want your code to be truly reusable, you need to consider the possibility that it will be used in a project with multiple entity managers.

This is what the Doctrine configuration of a Symfony2 project with multiple entity managers looks like (copied from the official documentation):

doctrine:
    dbal:
        default_connection: default
        connections:
            default:
                driver:   "%database_driver%"
                dbname:   "%database_name%"
                ...
            customer:
                driver:   "%database_driver2%"
                dbname:   "%database_name2%"
                ...
    orm:
        default_entity_manager: default
        entity_managers:
            default:
                connection: default
                mappings:
                    AcmeDemoBundle:  ~
                    AcmeStoreBundle: ~
            customer:
                connection: customer
                mappings:
                    AcmeCustomerBundle: ~

This particular configuration defines two different database connections used by two different entity managers which manage entities from different bundles.

Now let's say you release the CustomerManager class from the example above as part of the open source bundle AcmeCustomerBundle. You add the following service definition in Acme/CustomerBundle/Resources/config/services.yml:

services:
    acme_customer.customer_manager:
        class: Acme\CustomerBundle\Doctrine\CustomerManager
        arguments:
            - @doctrine.orm.default_entity_manager

However, when this bundle is installed in a project with the Doctrine configuration above, the entities in the AcmeCustomerBundle will be managed by the customer entity manager (which is available as the doctrine.orm.customer_entity_manager service), not by the default entity manager. This means that the entity manager that is being injected in the CustomerManager class is the wrong one: it is not capable at all to manage the Customer entity from the AcmeCustomerBundle.

Since you can not know in advance which entity manager the user of your bundle wants to use, you need to remove the hard dependency on the default entity manager. Instead you should depend on the ManagerRegistry, which knows all about the available entity managers in a project and is able to find the right entity manager for a given entity class.

In fact, when you use $this->getDoctrine() in your controller, you already receive an instance of the ManagerRegistry interface. When you call its method getManager(), it returns the default entity manager.

The ManagerRegistry also has a method getManagerForClass($class) which tries to find an entity manager which is able to manage entities of the given class. When we ask it to find the entity manager for the Acme\CustomerBundle\Entity\Customer class, it returns the customer entity manager. When one of the other entity classes is provided, it returns the default entity manager.

In practice this means that classes that need an entity manager, should not get an EntityManager instance injected, but instead should receive an ManagerRegistry instance:

use Doctrine\Common\Persistence\ManagerRegistry;

class CustomerManager
{
    private $managerRegistry;

    public function __construct(ManagerRegistry $managerRegistry)
    {
        $this->managerRegistry = $managerRegistry;
    }

    public function createCustomer(Customer $customer)
    {
        // retrieve the right entity manager for this type of entity
        $entityManager = $managerRegistry->getManagerForClass(get_class($customer));

        $entityManager->persist($customer);
        $entityManager->flush();
    }
}

Finally the service definition of CustomerManager needs to be modified: the constructor argument should not be doctrine.orm.default_entity_manager anymore, instead it should be doctrine, which is the ManagerRegistry containing all the entity managers known inside the current application.

services:
    acme_customer.customer_manager:
        class: Acme\CustomerBundle\Doctrine\CustomerManager
        arguments:
            - @doctrine

We have accomplished maximum reusability since the CustomerManager can now be used in projects with multiple entity managers.

Bonus: support for different object managers

As you may have noticed, the ManagerRegistry interface was imported from the Doctrine Common library. Its interface says that the getManager*() methods return an instance of ObjectManager (which is also part of the Doctrine Common library). All the object managers from Doctrine persistence libraries like Doctrine ORM, Doctrine MongoDB ODM, Doctrine CouchDB ODM, etc. implement this ObjectManager interface, which itself offers methods like persist() and flush(). This means that once you make your classes depend on the ManagerRegistry interface instead of the EntityManager class, you have made them reusable in many other contexts than just the context of a project that uses Doctrine ORM for persistence.

Take a look at the FOSUserBundle and see how they solved the problem of different persistence libraries. Also take a look at the interfaces and base classes in the Doctrine Common Persistence sub-library. This will make you aware of all the "cheap" possibilities of making your persistence-related code even more reusable.

PHP Symfony Doctrine ORM persistence reuse
Comments
This website uses MailComments: you can send your comments to this post by email. Read more about MailComments, including suggestions for writing your comments (in HTML or Markdown).
Victor Toulouse

For some reason, this doesn't work for me. The method simply returns the Entity Manager that comes first in the Doctrine configuration. I've posted a question on Stack Overflow with the details, but I haven't got an answer yet. http://stackoverflow.com/qu...What type of mapping did you use for your entities? Annotations? XML?UPDATE: solved it. When using annotations, you need to set the prefix parameter in the Doctrine configuration for the Annotation Driver to limit its scope to the desired namespace.

I am in a situation where I will have entities of the same class, but in different managers.
Am I still able to make use of this?
It does not sound like it since it seems to be searching for the EM by the class_name.

If not, any suggestions on how I can do this, and if it's possible to get the EM from the an object that is not yet persisted.

Thanks.

forsberg

That's why I prefer to go a slighly different routine that the author of the article suggests: to use actually the ObjectManager interface (also mentioned in the article). Then I inject any kind of persistance manager I want: PHPCR-ODM (NoSQL Document Manager), ORM (Doctrine Entity Manager), or anything else which implements the ObjectManager. Then, it's sufficient to just relpace the Object Manager in the services config (yml, xml...) file. The only thing is that you need to remember to use only the ObjectManager interface's methods, and not Entity Manager's one.

Nelson da Costa

Not really a comment, great article, as always. You added a note in blue kust under the post title, shouldn't it say "Please also read my next article about injecting repositories."?

stalxed

I inject Doctrine\Common\Persistence\ManagerRegistry.
Work fine, thank you!

But how can I now use transaction mechanism?

Marc Morera Merino

[Answer edited]

Just a question, Matthias.

Lets assume I know the type of entities I want to manage in a service, and lets assume I can inject the full repository using ExpressionLanguage, which means I can find correct manager using the entity namespace stored in a parameter.

If a service is designed to persist more than one different entity types, what strategy should we follow? Inject one manager per each Entity?

This scenario tells us that a service should only manage one entity type?

Matthias Noback

I'd say one service per entity type, even though it's the same service. It may become a different service at some point. Also read my follow-up post on this subject, in most cases it's probably better to inject a repository.

Marc Morera Merino

Yes, I'm used to injecting just the repository using ExpressionLanguage when I do not need entityManager for anything else. But is just an specific scenario.

If the full entityManager is needed, IMO a very good solution is injecting it using also the ExpressionLanguage, so is not bussiness logic but service definition ( And better for unit testing )

I like your posts, very very educational :) Thanks!

Lukas Lukac

Interesting Matthias! Thanks

Guest

Thanks great article.
In the most case I prefer injecting EntityManager instead of ManagerRegistry

why not inject EntityManager by using the expression language???

Marco Pivetta

tl;dr: the ObjectManager and the ManagerRegistry are locators. Don't inject them: instead, inject what you would fetch from them!

Injecting a registry (which is just a particular locator) is pretty much always a bad idea.

This approach is better than injecting the ObjectManager, as it allows for usage of different storage layers in a single service, but it is still pretty bad.

Instead, rely solely on constructor injection of the ObjectRepository (you should have your own interface for your repositories) for the specific entity type that you are dealing with. This requires more complex injection, but is a fairly simple enhancement that eases development on the runtime and makes dependencies much more readable and explicit.

Guest

What happens if you need exactly the EntityManager and not some specific repository? Should I still not inject that?

Marco Pivetta

If you need the EntityManager explicitly, then inject that one, though I'd say that it's a very rare case. Reconsider why you are doing that.

lsmith77

BTW one of the reasons why we created the registry was to deal with cases when a flush fails. After a failed flush, the entity/document is no longer useable since it is now in an undefined state, so it is closed to prevent harm. However this can be problematic for code following. So the registry makes it possible to get a fresh copy.

But yes it is a locator, especially if you follow a quite common pattern of injecting the name of a manager as well, ie. not just always using the default manager. I am honestly not yet sure how to best deal with all of this yet.

lsmith77

oh in general the fact that the UOW leaks across services and subrequests has lots of iffy side effects .. then again doing away with this fact is maybe not really feasible in terms of performance (the extreme would be creating new connections all over the place).

Matthias Noback

Thanks for your comments Lukas, I've written some more about this in the follow-up post. I'm curious what would happen with this when a Symfony application would be used as a continuously running server, like some people have proposed.

Alex Stansfield

This is kinda the problem I had last week, a cilex application that watches a beanstalk tube and processes the jobs.

I had the problem (although manufactured myself in order to try and make the code as robust as possible) where a SQL error during a transaction caused the entitymanager in the worker class to close.

Symfony2 has access to the resetManager method in the doctrine service that's in the container, so at least if you get back to somewhere you can access the container you can reset the Entity Manger and do more work with it.

This isn't available in cilex and I couldn't see any easy way to coax a new version of the entitymanager out of the provider that sets up doctrine as it just creates the entitymanagers.

In the end I managed, in a hack I'm not particularly proud of, to clone the em before the worker starts it's transaction and on sql error it uses the clone to find another fresh copy of the entity it needs to clean up and flush it. But then without a way to reset the main entity manager I was left with just exiting the application and letting supervisor restart it. A very unsatisfactory solution.

Having read your article I'm now wondering about forking the doctrine orm provider for cilex to provide a concrete RegistryManager in the container so I can reset the entitymanager and not have to exit.

Matthias Noback

That should be just very easy, I've almost done this once. The Doctrine common library contains already an abstract implementation of a ManagerRegistry if I remember correctly.

Alex Stansfield

Indeed it does, but I was hoping for a solution that didn't require rewriting the doctrine orm provider for cilex/silex/pimple (http://goo.gl/U0KjYE). Whilst it would probably benefit from having an implementation of ManagerRegistry I don't really have the time to do it myself right now. In fact making it work with the Pimple DIC looked like it could be pretty complicated when you want to reset an entity manager.

sznapka

That's right. Also I would inject the repository with save method, which uses entity manager to persist and flush entity (_em). In such situation you abstract data access layer in a way, which is easily to exchange for other implementation (mongo, restfull, etc.)

Guest

But isn't that a violation of the SRP, according to the Domain-Driven Design definition of a repository? I mean, should a repository be saving stuff?

Oleg Abrazhaev

Good point. I think it's better to add DomainSession object which will be wrapper around em and add save() method into it. And use it separately.

Matthias Noback

That's a good idea, thanks. I will write some more about this tomorrow.

Massimiliano Arione

I don't get it: what am I supposed to fetch when I call $this->objectManager->persist($entity)?

Matthias Noback

Marco is talking about fetching a repository from the entity manager.

Massimiliano Arione

It's OK for repos. But what about persisting and flushing? I still need ObjectManager for that

Matthias Noback

Also see my next post: you can easily add a save() method to the repository which does exactly that (persist, flush). From now on this would be my preferred way at least.

Guest

But isn't that a violation of the SRP, according to the Domain-Driven Design definition of a repository? I mean, should a repository be saving?

Daniel Ribeiro

But isn't that a violation of the SRP, according to the Domain-Driven Design definition of a repository? I mean, should a repository be saving things?

Matthias Noback

Agreed, save() is not such a good name in this case. Traditionally it's called add(), which better fits the domain terminology.

Matthias Noback

Thanks Marco. I completely agree. Most situations don't require the manager registry, nor the object manager itself. Those situations that do should use the strategy described in my post. I will definitely write some more about the other use cases that you describe in another post.