question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[ASK] How to use different EntityManagers / ManagerRegistry

See original GitHub issue

Is it possible to somehow “change” the used ManagerRegistry in default CollectionDataProvider & ItemProvider?

I’m using multiple EntityManagers, and each one using a custom DBAL connection and each EM maps the same “entities”. Like this:

doctrine:
    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

            sql.bar:
                host:     "%db_bar_host%"
                dbname:   "%db_bar_name%"
                user:     "%db_bar_user%"
                password: "%db_bar_password%"
                charset:  UTF8

            sql.foo:
                host:     "%db_foo_host%"
                dbname:   "%db_foo_name%"
                user:     "%db_foo_user%"
                password: "%db_foo_password%"
                charset:  UTF8
    orm:
        auto_generate_proxy_classes: "%kernel.debug%"
        entity_managers:
            default:
                connection: default
                mappings:
                    AppBundle: ~
            foo:
                connection: sql.foo
                mappings:
                    MyAwesomeBundle: ~
            bar:
                connection: sql.bar
                mappings:
                    MyAwesomeBundle: ~

I read the issue #226 that gives me a hint about how to do this but it seems applicable only to Write actions, and I need to every action (read & write).

I’m searching some way to change the $manager = $this->managerRegistry->getManagerForClass($resourceClass); of each DataProvider and use one that I could change according another service, maybe.

Any thoughts would be appreciated and thank you for this awesome library!

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:16 (6 by maintainers)

github_iconTop GitHub Comments

4reactions
byhoratisscommented, Feb 7, 2018

Finally I get it working as I wanted without changing anything into Api-platform. This is how I did it, just to help others that maybe would face the same problem.

For every database connection and server I used the proper DBAL configuration and also added my own “DynamicConnection” that will be used for the API.

doctrine:
    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

            sql.dynamic:
                wrapper_class: MyAwesome\Bundle\Doctrine\Connection\DynamicConnection
                charset:  UTF8

            sql.bar:
                host:     "%db_bar_host%"
                dbname:   "%db_bar_name%"
                user:     "%db_bar_user%"
                password: "%db_bar_password%"
                charset:  UTF8

            sql.foo:
                host:     "%db_foo_host%"
                dbname:   "%db_foo_name%"
                user:     "%db_foo_user%"
                password: "%db_foo_password%"
                charset:  UTF8

When I’ve to define my EntityManagers I gave them a name for every one and added ONE more just to be used through the API. Of course you can’t change which one will be used, but in my case, when the ManagerRegistry::getManagerForClass is called, “api” entity manager will be used and the dynamic connection will use the right connection. How? Just naming it alphabetically so in my list of EntityManagers, “api” is the first one that maps the same Bundles as the others.

The others EntityManagers are necessary to be able to use them by specifing the EM trough a console command or Controller or whatever, without the need of the bussiness logic of “dbSwitcher” service dependency of the wrapperClass.

    orm:
        auto_generate_proxy_classes: "%kernel.debug%"
        entity_managers:
            default:
                connection: default
                mappings:
                    AppBundle: ~

            # Uses a Dynamic connection
            # Naming is Important: the first that matches the Bundle will be used as EM inside ManagerRegistry
            api:
                connection: sql.dynamic
                mappings:
                    MyAwesomeBundle: ~
                    
            foo:
                connection: sql.foo
                mappings:
                    MyAwesomeBundle: ~
            bar:
                connection: sql.bar
                mappings:
                    MyAwesomeBundle: ~

The database connection is configured by my custom wrapperClass, which uses the parameters of others connections. This wrapper uses a Service that holds the bussiness logic (user, roles & stuff) to know which connection has to be used. Not sure if this solution is the right one, but still, this is the wrapper:

namespace MyAwesome\Bundle\Doctrine\Connection;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Event\ConnectionEventArgs;
use Doctrine\DBAL\Events;
use MyAwesome\Bundle\Services\DbSwitcher;

class DynamicConnection extends Connection
{
    /** @var DbSwitcher */
    protected $dbSwitcher;

    /**
     * @var bool
     */
    private $_isConnected = false;

    /**
     * The parameters used during creation of the Connection instance.
     *
     * @var array
     */
    private $_params = array();

    /**
     * @param DbSwitcher $dbSwitcher
     */
    public function setDbSwitcher(DbSwitcher $dbSwitcher)
    {
        $this->dbSwitcher = $dbSwitcher;
    }

    /**
     * @inheritDoc
     */
    public function connect()
    {
        $container = $this->dbSwitcher->getContainer();
        
        $connection = $container->get(sprintf('doctrine.dbal.sql.%s_connection', $this->dbSwitcher->getWorkingEntityManagerName()));

        $this->_params = $connection->getParams();

        if ($this->_isConnected) {
            return false;
        }

        $driverOptions = isset($this->_params['driverOptions']) ?
            $this->_params['driverOptions'] : array();
        $user = isset($this->_params['user']) ? $this->_params['user'] : null;
        $password = isset($this->_params['password']) ?
            $this->_params['password'] : null;

        $this->_conn = $this->_driver->connect($this->_params, $user, $password, $driverOptions);
        $this->_isConnected = true;

        if (false === $this->isAutoCommit()) {
            $this->beginTransaction();
        }

        if ($this->_eventManager->hasListeners(Events::postConnect)) {
            $eventArgs = new ConnectionEventArgs($this);
            $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs);
        }

        return true;
    }
}

And of course the proper CompilerPass to inject my own service to the Wrapper:

namespace MyAwesome\Bundle\Doctrine\DependencyInjection\CompilerPass;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class DynamicConnectionCompilerPass implements CompilerPassInterface
{
    /**
     * @inheritDoc
     */
    public function process(ContainerBuilder $container)
    {
        $container
            ->getDefinition('doctrine.dbal.sql.dynamic_connection')
            ->addMethodCall('setDbSwitcher', [
                new Reference('myawesomebundle.dbswitcher')
            ]);
    }

}

Sorry for the long post, but I hope it help others. Any ideas would be welcome and thank you again!

1reaction
denis-gorincommented, Jul 3, 2020

my way is

App\Entity\MyEntity.php

/**
 * @ApiResource()
 * @ORM\Entity(repositoryClass=MyEntityRepository::class)
 * @ORM\Table("MyEntity")
 */
class Company
{
...

App\Repository\MyEntityRepository.php

namespace App\Repository;

use App\Entity\MyEntity;
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\ORM\EntityRepository;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface;

class CompanyRepository extends EntityRepository implements ServiceEntityRepositoryInterface
{
    public function __construct(ManagerRegistry $registry)
    {
        $manager = $registry->getManager('NOT_DEFAULT_entity_manager');
        parent::__construct($manager, $manager->getClassMetadata(MyEntity::class));
    }
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

How to use different Entity Managers for ... - Stack Overflow
I have a few Entity Managers created in my services.yaml , each having its own DBAL connection to different servers.
Read more >
How to Work with multiple Entity Managers and Connections
You can use multiple Doctrine entity managers or connections in a Symfony application. This is necessary if you are using different databases or...
Read more >
Inject the ManagerRegistry instead of the EntityManager
This way you can persist different types of entities in your project in different database. Though I guess most projects use just one...
Read more >
Multiple Entity Managers in Doctrine
Within our code, this is pretty simple to use. Rather typehinting our EntityManager directly, we typehint the ManagerRegistry or our ConnectionRegistry and ...
Read more >
The ideal ways of using a repository classes in Symfony
I previously wrote a similar topic but I would suggest you to use this ... $entityManager;; public function __construct(ManagerRegistry ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found