Chadwick Meyer
Chadwick Meyer

Reputation: 7381

Symfony2 - How to Dynamically get a Doctrine entity's Entity Manager

We are building a CMS and website building platform with a lot of different vendors and clients sites, so there are a lot of different content type entities, which are edited by generic controllers and helper services that don't necessarily (easily) know what the entity manager is for a given entity.

NOTE: We have several entityManagers to separate access to different databases, e.g. Global, Billing, Local, etc

There are many cases where we need to detect what is the entity's EntityManager. For example, we have a MediaHelper that dynamically associates media from a database with matching fields on the entity (this doesn't work with associations because the Media has to connect with literally any entity and you can't have that kind of dynamic association and we don't want a hundred different associations).

The media is in a bundle managed by the 'Local' EntityManager. But the entity may be in a 'Global' EntityManager (you can't assume it's in the same entity manager). So we need to detect and persist the right entity manager for the right entity.

So how do you recommend dynamically detecting the entityManager for an entity?

Original Custom Method

NOTE: the accepted answer is a much better solution. This is just here for archival purposes.

Here is a simple solution that works. But I don't know enough about Symfony and Doctrine to know if it's a bad idea? Does anyone else know? If not, I don't know why this wouldn't be in the core, as a Doctrine Utility.

I created an EntityHelper service that injects the Doctrine service into it:

gutensite_cms.entity_helper:
    class: Gutensite\CmsBundle\Service\EntityHelper
    arguments:
        - "@doctrine"

Then in the entity helper is one simple function to get the Entity Manager for an entity (the config.yml registers the bundles for entity managers already):

/**
 * Automagically find the entityManager for an entity. 
 * @param $entity
 * @return mixed
 */
public function getManagerForEntity($entity) {
    $className = \Doctrine\Common\Util\ClassUtils::getRealClass(get_class($entity));
    foreach (array_keys($this->doctrine->getManagers()) as $name) {
        if(in_array($className, $this->doctrine->getManager($name)->getConfiguration()->getMetadataDriverImpl()->getAllClassNames())) return $em;
    }
}

NOTE: Doctrine Registry#getAliasNamespace already does something nearly identical to this foreach loop, I just modified the idea to return the entity manager instead of the namespace, e.g.

public function getAliasNamespace($alias) {
    foreach (array_keys($this->getManagers()) as $name) {
        try {
            return $this->getManager($name)->getConfiguration()->getEntityNamespace($alias);
        } catch (ORMException $e) {
        }
    }
    throw ORMException::unknownEntityNamespace($alias);
}

Update 10/21/15: Entity Detection Code updated per @Cerad's suggestion.

Upvotes: 2

Views: 1874

Answers (2)

Chadwick Meyer
Chadwick Meyer

Reputation: 7381

Per the suggestion of @qooplmao, there is an easy method already in the Doctrine core.

// 1) get the real class for the entity with the Doctrine Utility.
$class = \Doctrine\Common\Util\ClassUtils::getRealClass(get_class($entity))

// 2) get the manager for that class. 
$entityManager = $this->container->get('doctrine')->getManagerForClass($class);

Updated 10/22/15 After suggestions from Cerad and Qooplmao

Upvotes: 1

Wilt
Wilt

Reputation: 44422

Can you not give the entity manager a alias that suits the context it is for? You talk about Global, Billing, Local, so for example:

'service_manager' => array(
    'aliases' => array(
        'global_entity_manager' => 'My\Global\EntityManager',
        'billing_entity_manager' => 'My\Billing\EntityManager', 
        'local_entity_manager' => 'My\Local\EntityManager',
    ),
)

You could also map the entity manager to the namespace of the entity. So let's say you have a folder for your Global entities that is Global\Entity, then you could alias the entity manager for those entities Global\Entity\EntityManager. The advantage of this solution is that you can map several namespaces to the same entity manager, so your Billing and Global entities can easily share the same entity manager:

'service_manager' => array(
    'aliases' => array(
        'Global\Entity\EntityManager' => 'My\Global\EntityManager',
        'Billing\Entity\EntityManager' => 'My\Global\EntityManager', // <-same name
        'Local\Entity\EntityManager' => 'My\Local\EntityManager',
    ),
)

This only works if your entities in one namespace are manager by the same EntityManager instance. I can hardly believe this would not be the case in any project, but otherwise you should maybe reorganize a bit? :D

Upvotes: 0

Related Questions