Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

With multiple Entity Managers (EM) the Repository uses only the mapped EM for the Entity #9878

Closed
bigs21 opened this issue Jun 4, 2018 · 42 comments
Labels
actionable Clear and specific issues ready for anyone to take them. Doctrine
Milestone

Comments

@bigs21
Copy link
Contributor

bigs21 commented Jun 4, 2018

In the manual page How to Work with multiple Entity Managers and Connections for Symfony 4, we can see in the end the following code snippet:

        // Retrieves a repository managed by the "customer" em
        $customers = $this->getDoctrine()
            ->getRepository(Customer::class, 'customer')
            ->findAll()
        ;

Using the ORM configuration code indicated in the beginning of that document, where the Customer entity is mapped to the 'customer' entity manager, the 'customer' parameter for the getRepository() method is ignored.
As so, this last code about repository calls should be updated.

Also, I suggest to add the code on how to use multiple entity managers for a single Entity, or to add a note that it's not possible if that's the case.

@xabbuh
Copy link
Member

xabbuh commented Jun 7, 2018

If this is not working as described, the issue should be reported to the Doctrine issue tracker where the implementing code is located. Though actually I do not see why that should not be working as described.

@xabbuh xabbuh added the Doctrine label Jun 7, 2018
@bigs21
Copy link
Contributor Author

bigs21 commented Jun 29, 2018

Well, after all, it was, as after a few more tests I found out that I had an issue with where the Entities were placed: instead of having them in 'src/Entity/Main' and 'src/Entity/Customer', I had them in 'src/Entity' and 'src/EntityCustomer', with them being configured in that order.

With that, Doctrine was mapping the customer entities to the first (and default) entity manager.

Anyway, from what I've found out that the second parameter to getRepository is currently ignored, as there's no such parameter (is this from a previous version of symfony?).

As so, we can say the following:

        // Retrieves a repository managed by the "customer" em, the Customer's entity em
        $customers = $this->getDoctrine()
            ->getRepository(Customer::class)
            ->findAll()
        ;

        // Retrieves a repository managed by the "customer" em
        $customers = $this->getDoctrine()
            ->getRepository(Customer::class, 'customer')
            ->findAll()
        ;

So, the entity manager used in the repository is always the mapped one of that Entity, and by mapping the entities to a given em, we can't change a different connection to access some Entity, as it will only use the connection for it's em.
That comes from a use case where we were trying to use different connections (different databases) to access the same Entity (the same table structure), but in the same request always use the same connection. For example, depending on some parameter the request would use connection_A or connection_B.

I propose to add a note in the page specifying that it's not possible to use different entity managers to access the same Entity.

@HeahDude HeahDude added the actionable Clear and specific issues ready for anyone to take them. label Jul 1, 2018
@HeahDude HeahDude added this to the 2.8 milestone Jul 1, 2018
@javiereguiluz
Copy link
Member

@xabbuh what do you propose to do here? If it's true that getRepository() ignores the second argument ... shouldn't we just remove the second example? Thanks!

@xabbuh
Copy link
Member

xabbuh commented Jul 12, 2018

Yes, I think we shouldn't have it.

@delorie
Copy link

delorie commented Aug 30, 2018

Hello,

A brief introduction to our system:
The mission: we want to import data from a MSSQL database into our new application.

We have a vagrantbox, in there running the new application.
And a docker for the MSSQL database.
Now we have an Import Symfony Command Script on hostsystem that connects to the MSSQL database (Docker)
and to the Maria database (Vagrantbox) via SSH 3333:172.0.0.1:3306.

Our Doctrine Config

doctrine:
    dbal:
        default_connection: default
        types:
            uuid_binary_ordered_time: Ramsey\Uuid\Doctrine\UuidBinaryOrderedTimeType
        connections:
            default:
                # configure these for your database server
                driver: 'pdo_mysql'
                [...]
                url: '%env(resolve:DATABASE_URL)%' # Port 3306
                [...]
            command:
                driver: 'pdo_mysql'
                [...]
                url: '%env(resolve:COMMAND_DATABASE_URL)%' # Port 3333
                [...]

    orm:
        auto_generate_proxy_classes: '%kernel.debug%'
        [...]
        default_entity_manager: default
        entity_managers:
            default:
                connection: default
                naming_strategy: doctrine.orm.naming_strategy.underscore
                mappings:
                    App:
                        is_bundle: false
                        type: annotation
                        dir: '%kernel.project_dir%/src/Entity'
                        prefix: 'App\Entity'
                        alias: App
            command:
                connection: command
                naming_strategy: doctrine.orm.naming_strategy.underscore
                mappings:
                    App:
                        is_bundle: false
                        type: annotation
                        dir: '%kernel.project_dir%/src/Entity'
                        prefix: 'App\Entity'
                        alias: AppCommand

In our Command Script:

$doctrine = $this->container->get('doctrine');
$doctrine->getRepository(Salutation::class, 'command')->findAll()

// But Doctrine use the default EntityManager

We found in this File:
vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php
Doctrine\Common\Persistence\AbstractManagerRegistry

The method "getManagerForClass" ignores the defindet connection
getManagerForClass() (Line: 156)
[...]

foreach ($this->managers as $id) {
    $manager = $this->getService($id);

    if (! $manager->getMetadataFactory()->isTransient($class)) { // <-- first goal 
        return $manager;
    }
}

```php
[...]

// Debug output
$this = {Doctrine\Bundle\DoctrineBundle\Registry} [7]
...
 *Doctrine\Common\Persistence\AbstractManagerRegistry*managers = {array} [2]
  default = "doctrine.orm.default_entity_manager" // <-- first goal 
  command = "doctrine.orm.command_entity_manager"
...


The problem is just by Entitys with defined CustomRepositorys
```php
/**
 * @ORM\Table(name="salutations")
 * @ORM\Entity(repositoryClass="App\Repository\Person\SalutationRepository") // <-- Without CustomRepository it works
 */
class Salutation {}

Without CustomRepository it works because the Method "getRepository" in Class "ContainerRepositoryFactory" found no a $customRepositoryName so the call

return $this->getOrCreateRepository($entityManager, $metadata); (Line 77) // this works finde

but have the method found a CustomRepository the call
$repository = $this->container->get($customRepositoryName);

@pouetman87
Copy link

is there any news regarding the resolution of this issue?

@T123mutouren
Copy link

关于解决这个问题的消息有什么消息吗?

@andrmoel
Copy link

andrmoel commented Jan 4, 2019

Just don't use a custom repository. This will work.
But IMHO that's no solution. Its a bug, which should be fixed.

@m2payco
Copy link

m2payco commented Jan 15, 2019

I'm in the same situation. I want to use another entity manager to connect to a read-only replica of the original database using the same entities.

@pouetman87
Copy link

the only way that i found to use both custom repositories and more than one database is to use symfony version 3.4

@Floeig
Copy link

Floeig commented Jan 16, 2019

So far I have found this temporary solution for Symfony4

 private $registry;
    public function __construct(RegistryInterface $registry)
    {
        $this->registry = $registry;
        //parent::__construct($registry, MyEntity::class);
    }

    public function findAllByFieldName($customFieldName, $connection): array
    {
        $em = $this->registry->getEntityManager($connection);

        $customFields = $em->getRepository(MyEntity::class)->findOneBy(['Field_name'=>$customFieldName]);
   /* ...... */
}

Ugly but works..

@tech-no-logical
Copy link

this issue just cost me about a day's worth of debugging (I'm just starting out with symfony/doctrine so it took a while). together with a colleague we came to this workaround (we have two types of users, each with its own repositry and entitymanager)

namespace App\Repository;

use App\Entity\CustomUser;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Doctrine\ORM\EntityRepository;

class CustomUserRepository extends AbstractUserRepository
{
    public function __construct(RegistryInterface $registry)
    {
        $manager = $registry->getEntityManager('namedentitymanager');

        parent::__construct($registry, CustomUser::class);
        EntityRepository::__construct($manager, $manager->getClassMetadata(CustomUser::class));
    }
}

the thought being that the parent::__construct() puts in the default entity manager (the wrong one). the call to EntityRepository::__construct() fixes this.

@broiniac
Copy link
Contributor

broiniac commented Apr 13, 2019

Today I've run into similar problem. Long story short - I want to copy data from database no2 into database no1. Both are using same code base.

@andrmoel is right - only solution for now is to not use custom repositories.

The fastest way will be to use QueryBuilder: https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/query-builder.html#constructing-a-new-querybuilder-object

In another words - don't user getRepository method.

Example:

// get EntityManagers you want to use
$emTo = $registry->getManager();            // manager connected to "main" database
$emFrom = $registry->getManager('migrate'); // manager connected to other database

// create QueryBuilder for Entity from 'migrate' database
$emFrom->createQueryBuilder()
    ->select('a')
    ->from('App\Entity\Article', 'a')
;

// add some regular methods and conditions next

It looks easy (because it is) and obvious, but it wasn't easy to figure it out. Thank you guys for help!

@r2my
Copy link

r2my commented May 31, 2019

Hi! any news regarding this issue? I tried with Symfony 4.3.0, same issue...
Thanks for your help!

@xabbuh
Copy link
Member

xabbuh commented Jun 3, 2019

Can someone create a small example application that allows to debug? I would like to find out whether that's an issue in the documentation, in Symfony or in Doctrine, but I don't have such a use case in any of my applications.

@r2my
Copy link

r2my commented Jun 3, 2019

Hi! I created a new installation here:
https://github.com/r2my/symfomem
There are 3 EM: main, customer01 and customer02.
In ProductController, you'll see that 'customer01' and 'customer02' in getRepository() method are ignored.

@ekdevcenter
Copy link

ekdevcenter commented Aug 16, 2019

config/packages/doctrine.yaml

doctrine:
    dbal:
        default_connection: default
        connections:
            default:
                # configure these for your database server
                url: '%env(DATABASE_DB1_URL)%'
                driver: 'pdo_mysql'
                server_version: '5.7'
                charset: utf8mb4
            offerbdd:
                # configure these for your database server
                url: '%env(DATABASE_DB2_URL)%'
                driver: 'pdo_mysql'
                server_version: '5.7'
                charset: utf8mb4

    orm:
        default_entity_manager: default
        entity_managers:
            default:
                connection: default
                mappings:
                    App:
                        is_bundle: false
                        type: annotation
                        dir: '%kernel.project_dir%/src/Entity'
                        prefix: 'App\Entity\'
                        alias: App
            offerbdd:
                connection: offerbdd
                mappings:
                    Offer:
                        is_bundle: false
                        type: annotation
                        dir: '%kernel.project_dir%/src/Entity'
                        prefix: 'App\Entity\'
                        alias: Offer

My Repository

namespace App\Repository;

use App\Entity\Offer;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Symfony\Bridge\Doctrine\RegistryInterface;

/**
 * @method Offer|null find($id, $lockMode = null, $lockVersion = null)
 * @method Offer|null findOneBy(array $criteria, array $orderBy = null)
 * @method Offer[]    findAll()
 * @method Offer[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class OfferRepository extends ServiceEntityRepository
{
	private $registry;
	
    public function __construct(RegistryInterface $registry)
    {
    	$this->registry = $registry;
    }
	/**
	 * @return mixed
	 */
	public function findListedAll($connection)
	{
		$sql = "SELECT o FROM App\Entity\Offer o WHERE o.unlisted = :val OR o.unlisted IS NULL "
			 . "ORDER BY o.id DESC";
		
		return $this->registry->getEntityManager($connection)
			->createQuery($sql)
			->setParameter('val', false)
			->getResult();
	}
}

My Controller:

public function index(Request $request)
    {
	...
		    $offerEntityManager  = $this->getDoctrine()->getManager('offerbdd');
		    $offers = $offerEntityManager->getRepository(Offer::class)->findListedAll('offerbdd');
	    ....
    }

It's work!
Thanks to you tech-no-logical and to others for their contribution. It helped me a lot.

@soeren-helbig
Copy link

soeren-helbig commented Aug 22, 2019

Hey! Same here. Ended up in removing repositoryClass from mapping, but it feels a very strange!

Edit: another possible workaround

ArticleRepository extends ServiceEntityRepository
{
    ...
    public function setEntityManager(EntityManagerInterface $entityManager): self
    {
        $this->_em = $entityManager;

        return $this;
    }
   ....
}

e.g.

$activeEntityManager = ...;

$articles = $activeEntityManager
    ->getRepository('App:Article')
    ->setEntityManager($activeEntityManager)
    ->findAll()
;

@xabbuh for me it seems to be doctrine issue. Some debugging brought me to the same point as @delorie already showed in his post.

@OskarStark
Copy link
Contributor

@xabbuh did you already find some time to try the reproducer mentioned in #9878 (comment) ?

@xabbuh
Copy link
Member

xabbuh commented Aug 23, 2019

The example application didn't make use of the getRepository() method of the ManagerRegistry, but called the getRepository() method of a concrete entity manager instead. This cannot work as that method doesn't have the manager name argument.

However, if we change the code to actually use the manager registry, we will see no difference in the behaviour. This comes from the fact that ContainerRepositoryFactory class that is used behind the scenes will only find one service for the repository class. If we further changed the code to let the repository not extend the ServiceEntityRepository, we will finally see a change in the behaviour.

@ipernet
Copy link

ipernet commented Sep 4, 2019

I'm replying here on the subject of having multiple entity managers managing the same entities and the bug related to it.

However, if we change the code to actually use the manager registry, we will see no difference in the behaviour. This comes from the fact that ContainerRepositoryFactory class that is used behind the scenes will only find one service for the repository class. If we further changed the code to let the repository not extend the ServiceEntityRepository, we will finally see a change in the behaviour.

Agreed with the description @xabbuh but as @andrmoel said, we can consider there is still a bug here, because there is a different behavior based on whether the entity is defined with a custom repository and how this one is defined or whether no custom repository is used.

It's subtle because if the entity uses a custom repository extending the EntityRepository class, the behavior is different than if the repo extends the newer ServiceEntityRepository class.

Here is a repro project: https://github.com/ipernet/symfony-doctrine-multi-em-test

And the test command:

<?php

namespace App\Command;

use App\Entity\Cat;
use App\Entity\Dog;
use App\Entity\Duck;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\Common\Persistence\ManagerRegistry;

class SameEntityDualManagerCommand extends Command
{
    // the name of the command (the part after "bin/console")
    protected static $defaultName = 'app:test';

    /**
     * @var ManagerRegistry
     */
    private $registry;

    public function __construct(ManagerRegistry $registry)
    {
        $this->registry = $registry;
        $this->defautEm = $this->registry->getManager('default');
        $this->otherEm = $this->registry->getManager('em2');

        parent::__construct();
    }

    protected function configure()
    {
        // ...
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->output = $output;

        $this->testEntityWithNoCustomRepository();
        $this->testEntityWithNoCustomRepositoryUsingRegistryRepository();

        $this->testEntityWithCustomRepositoryExtendingServiceEntityRepository();
        $this->testEntityWithCustomRepositoryExtendingServiceEntityRepositoryUsingRegistryRepository();

        $this->testEntityWithCustomRepositoryExtendingEntityRepository();
        $this->testEntityWithCustomRepositoryExtendingEntityRepositoryUsingRegistryRepository();
    }

    /**
     *
     * @return void
     */
    private function testEntityWithNoCustomRepository()
    {
        $entityDefaultEm = $this->defautEm->getRepository(Dog::class)->findOneBy(['id' => 1]);
        $entityEm2 = $this->otherEm->getRepository(Dog::class)->findOneBy(['id' => 1]);

        $this->testResult($entityDefaultEm, $entityEm2);
        $this->output->writeln(' - '.__FUNCTION__);
    }

    /**
     *
     * @return void
     */
    public function testEntityWithNoCustomRepositoryUsingRegistryRepository()
    {
        $entityDefaultEm = $this->registry->getRepository(Dog::class, 'default')->findOneBy(['id' => 1]);
        $entityEm2 = $this->registry->getRepository(Dog::class, 'em2')->findOneBy(['id' => 1]);

        $this->testResult($entityDefaultEm, $entityEm2);
        $this->output->writeln(' - '.__FUNCTION__);
    }

    /**
     * @return void
     */
    private function testEntityWithCustomRepositoryExtendingServiceEntityRepository()
    {
        $entityDefaultEm = $this->defautEm->getRepository(Cat::class)->findOneBy(['id' => 1]);
        $entityEm2 = $this->otherEm->getRepository(Cat::class)->findOneBy(['id' => 1]);

        $this->testResult($entityDefaultEm, $entityEm2);
        $this->output->writeln(' - '.__FUNCTION__);
    }

    /**
     *
     * @return void
     */
    public function testEntityWithCustomRepositoryExtendingServiceEntityRepositoryUsingRegistryRepository()
    {
        $entityDefaultEm = $this->registry->getRepository(Cat::class, 'default')->findOneBy(['id' => 1]);
        $entityEm2 = $this->registry->getRepository(Cat::class, 'em2')->findOneBy(['id' => 1]);

        $this->testResult($entityDefaultEm, $entityEm2);
        $this->output->writeln(' - '.__FUNCTION__);
    }

    /**
     *
     * @return void
     */
    public function testEntityWithCustomRepositoryExtendingEntityRepository()
    {
        $entityDefaultEm = $this->defautEm->getRepository(Duck::class)->findOneBy(['id' => 1]);
        $entityEm2 = $this->otherEm->getRepository(Duck::class)->findOneBy(['id' => 1]);

        $this->testResult($entityDefaultEm, $entityEm2);
        $this->output->writeln(' - '.__FUNCTION__);
    }

    /**
     *
     * @return void
     */
    public function testEntityWithCustomRepositoryExtendingEntityRepositoryUsingRegistryRepository()
    {
        $entityDefaultEm = $this->registry->getRepository(Duck::class, 'default')->findOneBy(['id' => 1]);
        $entityEm2 = $this->registry->getRepository(Duck::class, 'em2')->findOneBy(['id' => 1]);

        $this->testResult($entityDefaultEm, $entityEm2);
        $this->output->writeln(' - '.__FUNCTION__);
    }

    private function testResult($entityDefaultEm, $entityEm2)
    {
        if (spl_object_hash($entityDefaultEm) === spl_object_hash($entityEm2)) {
            $this->output->write('<error>NOT OK: The two entities should not be the same object</error>');
        } else {
            $this->output->write('<info>OK: The two entities are different objects</info>');
        }
    }
}

image

@OskarStark
Copy link
Contributor

OskarStark commented Sep 4, 2019

Sorry for bothering you Andreas (@alcaeus ), but can you please have a look at this issue and the reproducers and may support us here? Thanks 🙏 🍺

@alcaeus
Copy link
Contributor

alcaeus commented Sep 4, 2019

No problem. Without reading through the entire issue, there used to be an issue in the manager registry where calling getRepository did not call getManagerForClass: doctrine/persistence#45. This was fixed in doctrine/persistence#48 which was released in 1.1.1 back in April. With this fixed, calling $this->getDoctrine()->getRepository(SomeEntity::class) should always return a repository for the correct manager.

To cut a lot of discussion short: this auto-detection only works when an entity is only managed by a single entity manager. If the same entity is managed by multiple entity managers, getManagerForClass (which is used internally when calling getRepository on the manager registry) will return the first entity manager it discovers that manages the class. In such cases, getRepository on the registry should always be called with a specific entity manager name.

In the future, we may consider throwing an exception in getManagerForClass if we can't find a single entity manager that manages a class. Since this is a BC break, I don't know when we'll be able to add this, but I've created doctrine/persistence#68 to track this.

One more issue that was highlighted here was that of service repositories. Keep in mind that the entire setup of service repositories has the "90%-use-case" in mind, which in this case would be a single entity manager, or at least having the entity only managed by a single entity manager. Once an entity is managed by multiple entity managers, you'd need to have multiple services for the repository, which the system currently doesn't handle. This is not to say that you can't use service repositories at all in such a scenario, you just need to create more glue code to work around these issues. Throwing an exception if an entity is managed by multiple entity managers would also fix this, as the repository service will no longer be instantiable, forcing the user to realise that they've taken a wrong turn somewhere.

So, to summarise:

  • If using multiple entity managers, try not to have an entity managed by multiple managers
  • If you do so, always specify the entity manager in question when calling getRepository on the registry
  • Also, don't use service repositories when managing the same entity with multiple managers. Instead, stick to "classic" repositories or write the glue code yourself.

@ipernet
Copy link

ipernet commented Sep 4, 2019

Hi @alcaeus

Thanks for your time and the very detailed information given here. I understand the limits of the "service repositories" you describe and so can only agree with the conclusion you draw.

II'll try to contribute to the improvement of the How to Work with multiple Entity Managers and Connections documentation to include the juicy bits of information we have here.

Thanks!

@alcaeus
Copy link
Contributor

alcaeus commented Sep 4, 2019

Awesome, thanks @ipernet!

@ipernet
Copy link

ipernet commented Nov 4, 2019

So, to summarise:

* If using multiple entity managers, try not to have an entity managed by multiple managers

* If you do so, always specify the entity manager in question when calling `getRepository` on the registry

* Also, don't use service repositories when managing the same entity with multiple managers. Instead, stick to "classic" repositories or write the glue code yourself.

Hi @alcaeus, as a possible answer to these limitations I have put an idea here: doctrine/DoctrineBundle#1044

@Floeig
Copy link

Floeig commented Nov 15, 2019

If using multiple entity managers, try not to have an entity managed by multiple managers

The project I have is to build an API to manage 3 db on 3 different sites. So I have to use 1 entity on 3 Managers.
I'll wait for the doc
Thanks :)

@erdincgc
Copy link

erdincgc commented Dec 2, 2019

After a year still documentation is not enough to cover this issue and still not working in a straight-forward way ... https://github.com/r2my/symfomem this repo is still not working.

Being told :
If you do so, always specify the entity manager in question when calling getRepository on the registry
is also not working as of 4.3.6

this is not an ignorable problem imho , at least a proper workaround should be added to docs to be prepared for future updates.

@ITtoponline
Copy link

Woh, I'm having the same problem with Symfony 4.4.
I managed entities that can be present on multiple connections.
Doing that does not work at all. I get the error that my table is undefined

$data_niveau = $this->doctrine->getRepository(DataNiveau::class, $connection)->findAll();

Works perfectly fine on SF 3.4. It completely break my application, cannot upgrade to SF 4.4 for now.

@gisoftlab
Copy link

This bug is reproduced over and over. Symfony 5.04 still has the same issue.

@HeahDude HeahDude modified the milestones: 2.8, 3.4 Feb 8, 2020
@Glioburd
Copy link

Same problem on a Symfony 3.4 app. The default connection is always used :(

@Ph0xEn
Copy link

Ph0xEn commented Mar 11, 2020

Hi everyone,

since Symfony 5 the provided workaround from @tech-no-logical (#9878 (comment)) does not longer work.

Has anybody a new solution? I'm struggling at this point.

Thank you very much!

@Glioburd
Copy link

Hi everyone,

since Symfony 5 the provided workaround from @tech-no-logical (#9878 (comment)) does not longer work.

Has anybody a new solution? I'm struggling at this point.

Thank you very much!

@Ph0xEn I managed to use a 2nd EM with @soeren-helbig's solution #9878 (comment). At least it works :).

@Ph0xEn
Copy link

Ph0xEn commented Mar 11, 2020

Hi @Glioburd,

thank you for the link. It works like a charm :)

@ahundiak
Copy link

ahundiak commented Jun 5, 2020

Very confused. These issues are a result of using the ServiceEntityRepository in order to support autowire. Extend your repository from EntityRepository, exclude your repositories from autowire and (if needed) go back to manually creating and injecting repository services.

@13bAdLuCk13
Copy link

13bAdLuCk13 commented Feb 19, 2022

Is the workaround of @soeren-helbig in his comment still working? Could someone give me a more explicit code-example? I can't get his code to run. Or is there already an official solution to use two entity managers for one entity?

@ahundiak
Copy link

ahundiak commented Feb 19, 2022

The DoctrineBundle no official solution to manage one entity type with multiple managers. It has been that way ever since the ServiceEntityRepository class was introduced and seems unlikely to change. Best approach is to simply not do that.

You can however:

  1. Have your repositories inherit from EntityRepository instead of ServiceEntityRepository
  2. Exclude your repositories from autowire
  3. Create a Doctrine\ORM\Repository\DefaultRepositoryFactory service
  4. Change the repository_factory config in doctrine.yaml from ContainerRepositoryFactory to DefaultRepositoryFactory

At this point you should be good to go as long as you are fine with always using $em->getRepository(Entity::class) to get your repositories. If you have the urge to inject the repositories as services then you need to manually define them using a factory service definition. It is what we used to do back in the pre-autowire days.

It is also instructive to compare the DefaultRepositoryFactory with the ContainerRepositoryFactory to see why it's difficult to support multiple entity managers with autowire.

One final note: I have sometime seen stuff about how resetting an entity manager does not work properly with the DoctrineBundle code. I don't do that sort of thing and I don't recall exactly what the problem was. Just be careful if you do happen to use reset.

@pouetman87
Copy link

I use this workaround : #9878 (comment).
I find this to be the simplest solution, And you can still easily use the default repository.

@ahundiak
Copy link

@pouetman87 Pretty sure that work around is not doing what you think it is. As long as the ContainerRepositoryFactory is being used then you will never get different repository instances. And swapping around which manager a repository belongs to seems like asking for trouble.

@danny-endeavour
Copy link

@imbachb
Copy link

imbachb commented Nov 25, 2022

Is there a way to add an entity manager in the doctrine config so that it is specifically NEVER used when injecting a repository that extends ServiceEntityRepository? In my case I have an entity manager that should always be used when injecting repositories, but for a special action I want to use a different entity manager that manages the same entities on another database.
The only difference between the two entity managers is the database url ... maybe there is an easier way to accomplish this?

@Gonzalo1987
Copy link

Gonzalo1987 commented Dec 23, 2022

What's about using the EntityRepository and load the parameters with:

my.entity.metadata:
    class: App\Entity\Metadata
    arguments: 
        $entityName: App\Entity\MyEntity

App\Repository\MyEntityRepository:
    arguments:
        [$class: my.entity.metadata]

This should solve the problem? A EntityRepository autowireable

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
actionable Clear and specific issues ready for anyone to take them. Doctrine
Projects
None yet
Development

No branches or pull requests