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

Allow multiple doctrine entity manager instances #99

Closed
wants to merge 2 commits into from
Closed

Allow multiple doctrine entity manager instances #99

wants to merge 2 commits into from

Conversation

Zayon
Copy link

@Zayon Zayon commented Sep 10, 2018

Fixes #90, based on #96

Copy link
Owner

@theofidry theofidry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm 👍 for the feature, thank you a lot for the PR! I would however:

  • Try the suggestion regarding the constructor without which it's a BC break and would require a version bump which I would like to avoid if possible
  • I would like to see a test for it as well; You can add it to ObjectManagerPersisterTest. The DB configuration can be found in autoload.php and a make test_doctrine_bridge should run all the right tests without you having to worry about setting up dependencies (it does require you to install a local DB instance though

@@ -71,7 +71,7 @@
<service id="fidry_alice_data_fixtures.persistence.persister.doctrine_mongodb.object_manager_persister"
class="Fidry\AliceDataFixtures\Bridge\Doctrine\Persister\ObjectManagerPersister"
lazy="true">
<argument type="service" id="doctrine_mongodb.odm.document_manager" />
<argument type="service" id="doctrine" />
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect this would be a different one no? doctrine is for the ORM Registry isn't it?

Same comment for the two occurrences below

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be doctrine_mongodb, same for the purger and purger_factory

@@ -30,7 +31,8 @@
{
use IsAServiceTrait;

private $objectManager;
/** @var array|ObjectManager[] */
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can omit array here

@@ -42,9 +44,9 @@
*/
private $metadata = [];

public function __construct(ObjectManager $manager)
public function __construct(ManagerRegistry $managerRegistry)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a more BC friendly way would be to inject directly the managers:

function __construct(ObjectManager ...$managers) {
  $this->managers = $managers;
}

Since the service is lazy it shouldn't cause any issue. This may require an extra dependency with the expression language though

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having the ManagerRegistry allow to call $this->managerRegistry->getManagerForClass($class); directly, wich remove a lot of complexity. See below

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah @jdeniau is right, having the ManagerRegistry is nice to remove some complexity.

What should we do ? ObjectManager ...$managers with some complexity but no BC or ManagerRegistry $managerRegistry with reduced complexity but BC. Up to you @theofidry .

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can the registry be instantiated here since we cannot inject it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instantiated ? You mean make a new ManagerRegistry() inside the constructor ? it seems hard, see the service declaration : https://github.com/doctrine/DoctrineBundle/blob/master/Resources/config/dbal.xml#L59L65

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the implementation: https://github.com/doctrine/persistence/blob/master/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php#L156

I don't think iterating over the managers is a bad idea in the end. Maybe you can create a custom ManagerRegistry class to be able to cache the results a bit like we are doing for the metadata, which you then can instantiate more easily

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about it again, what about have a dedicated class implementing ObjectManager?

class ObjectsManager implements ObjectManager {
  function (ManagerRegistry $registry) {
    $this->registry = $registry;
  }

  function getClassMetadata(string $class): Metadata {
    $manager = $this->managerRegistry->getManagerForClass($class)
    $manager->getClassMetadata($class);
  }
}

So that manager can take the registry just fine and we can inject this new manager without any BC breaks

Copy link
Contributor

@dkarlovi dkarlovi Oct 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think ObjectManager[] would be the best approach since it allows to push managers from both ORM and ODM together, you can iterate them and treat them the same regardless where they came from.

Nope, injecting managers alone is not enough since you can't ask the manager if it is managing a specific class, the only place where this info is available is the registry.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi. I don't know if my comment would help but injecting the registry and getting a fresh manager each time you want to persist will also allow insert errors. Usually as soon as the database throws an error, the manager closes. The only way to re-open the connection is to get a fresh manager from the registry. Keeping in mind this I would suggest to inject the registry instead of all the managers.

@theofidry
Copy link
Owner

Note: I'm trying to fix the tests on master (it should be failures related to deprecation notices, but the tests themselves are fine so you can still see the results locally)

Copy link

@jdeniau jdeniau left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to compare with master...jdeniau:jd-feat-multipleDoctrineOrmV2 if you need help

@@ -53,7 +55,7 @@ public function __construct(ObjectManager $manager)
public function persist($object)
{
if (null === $this->persistableClasses) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use $objectManager = $this->managerRegistry->getManagerForClass($class); with remove all the complexity of persistableClass.

It returns ObjectManager|null

@@ -42,9 +44,9 @@
*/
private $metadata = [];

public function __construct(ObjectManager $manager)
public function __construct(ManagerRegistry $managerRegistry)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having the ManagerRegistry allow to call $this->managerRegistry->getManagerForClass($class); directly, wich remove a lot of complexity. See below

} catch (ORMException $exception) {
if ($metadata->idGenerator instanceof ORMAssignedGenerator) {
throw ObjectGeneratorPersisterExceptionFactory::createForEntityMissingAssignedIdForField($object);
foreach ($this->managers as $manager) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then, as you have an entity manager, you just need to do $objectManager->persist($object).

@@ -130,8 +143,11 @@ private function getPersistableClasses(ObjectManager $manager): array
private function getMetadata(string $class): ClassMetadata
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here, you can inject the ObjectManager and get metadata directly with $objectManager->getMetadata($class)

@@ -73,7 +73,7 @@
<service id="fidry_alice_data_fixtures.persistence.persister.doctrine_phpcr.object_manager_persister"
class="Fidry\AliceDataFixtures\Bridge\Doctrine\Persister\ObjectManagerPersister"
lazy="true">
<argument type="service" id="doctrine_phpcr.odm.document_manager" />
<argument type="service" id="doctrine" />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be doctrine_phpcr, same with the purger and purger_factory

@@ -71,7 +71,7 @@
<service id="fidry_alice_data_fixtures.persistence.persister.doctrine_mongodb.object_manager_persister"
class="Fidry\AliceDataFixtures\Bridge\Doctrine\Persister\ObjectManagerPersister"
lazy="true">
<argument type="service" id="doctrine_mongodb.odm.document_manager" />
<argument type="service" id="doctrine" />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be doctrine_mongodb, same for the purger and purger_factory

@Zayon
Copy link
Author

Zayon commented Sep 11, 2018

I've made an update to the PR, it's still WIP but suggestions are welcomed.

  • Test it works with one manager
  • Test it works with multiple managers
  • Change Purgers and PugerFactories
  • Add a phpunit test


/**
* @var ClassMetadata[] Entity metadata, FQCN being the key
* @var string[ObjectManager] Keys are FQCN of persistable objects, Values are managers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that's a valid phpdoc type.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I don't know if it's possible to set the type of keys


public function __construct(ObjectManager $manager)
public function __construct(ObjectManager ...$managers)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technically, this allows to pass no manager at all (a variable argument is 0+, not 1+)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well this was a suggestion from @theofidry as I injected the ManagerRegistry in my first commit.
With this and the fact that we can't inject %doctrine.entity_managers% it seems it's not possible to use ObjectManager ...$managers. Any thought ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, if you want to keep a variadic constructor, you will need an argument to add all the relevant references as arguments in your service definition.

$this->managers = $managers;

foreach ($managers as $manager) {
$allMetadata = $manager->getMetadataFactory()->getAllMetadata();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loading all metadata of a manager is bad for performance, as this is not an optimized API. The optimized API is the one getting metadata for a given class (as the class name is the cache key).

It would be better to build the map on demand, when being asked for a specific class, rather than trying to build it upfront

@@ -71,7 +72,7 @@
<service id="fidry_alice_data_fixtures.persistence.persister.doctrine_mongodb.object_manager_persister"
class="Fidry\AliceDataFixtures\Bridge\Doctrine\Persister\ObjectManagerPersister"
lazy="true">
<argument type="service" id="doctrine_mongodb.odm.document_manager" />
<argument>%doctrine_mongodb.odm.document_managers%</argument>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that won't work. As you made the constructor variadic, you need to add many arguments.

and btw, this parameters contains the names of document managers, not the objects (a parameter cannot contain references to services)

@@ -79,7 +80,7 @@
<service id="fidry_alice_data_fixtures.persistence.persister.doctrine.object_manager_persister"
class="Fidry\AliceDataFixtures\Bridge\Doctrine\Persister\ObjectManagerPersister"
lazy="true">
<argument type="service" id="doctrine.orm.entity_manager" />
<argument>%doctrine.entity_managers%</argument>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same issue here than for the ODM

@@ -73,7 +75,7 @@
<service id="fidry_alice_data_fixtures.persistence.persister.doctrine_phpcr.object_manager_persister"
class="Fidry\AliceDataFixtures\Bridge\Doctrine\Persister\ObjectManagerPersister"
lazy="true">
<argument type="service" id="doctrine_phpcr.odm.document_manager" />
<argument>%doctrine_phpcr.odm.document_managers%</argument>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and again here

@stof
Copy link
Contributor

stof commented Sep 12, 2018

Btw, the issues I reported are telling me that I don't agree with your checked "Test it works with one manager" (and Travis agrees with me, not with you, as CI is failing)

@Zayon
Copy link
Author

Zayon commented Sep 12, 2018

Btw, the issues I reported are telling me that I don't agree with your checked "Test it works with one manager" (and Travis agrees with me, not with you, as CI is failing)

You are right, too much multi-tasking made me forgot to change the service declaration in my test env :(

@Zayon
Copy link
Author

Zayon commented Sep 24, 2018

@theofidry Any idea on what is the cause of the fail ?

@theofidry
Copy link
Owner

No idea, would need to dig into it but cannot at the moment. I would suggest to carry on and I can dig into it after the reviews to see what is causing an issue

@jdeniau
Copy link

jdeniau commented Sep 24, 2018 via email

@theofidry
Copy link
Owner

Yes, you could throw a LogicException on those at first and we can change it later on whenever we want to

@dkarlovi
Copy link
Contributor

This looks like it's mostly finished or am I missing something?

@theofidry
Copy link
Owner

It still needs https://github.com/theofidry/AliceDataFixtures/pull/99/files#r219945408 + making the tests greens, but otherwise yeah we are not far off having it merged

@dkarlovi
Copy link
Contributor

Note, my first reason for asking about this was to allow me to load ORM and ODM fixtures at the same time (ODM references ORM).

Looking at hautelook:fixtures:load, it explicitly requires an ORM manager which means it's unusable in that regard? Would this mean adding a new command is the only way currently?

@dkarlovi
Copy link
Contributor

BTW the ODM documents are already properly constructed by hautelook:fixtures:load, they're just not flushed, which is quite unfortunate.

@theofidry
Copy link
Owner

theofidry commented Oct 11, 2018

Would this mean adding a new command is the only way currently?

Yes, and potentially can be switched to be the new default command, i.e. orm, odm stay specific, but the default and general one loads everything, ORM, ODM and every manager

BTW the ODM documents are already properly constructed by hautelook:fixtures:load, they're just not flushed, which is quite unfortunate

I think there is an open issue about the ORM not having its manager properly propagated, i.e. the right manager is used for loading but not purging. I'm not aware of an issue for the ODM so if there is one an issue should be opened for it

@dkarlovi
Copy link
Contributor

Here's a quick hack to illustrate the required points, continuation from our Slack conversation:
https://github.com/hautelook/AliceBundle/pull/440

For the feature to work, the new DoctrineLoader will need to have manager registries injected. I can just do the entire thing here, but it does reduce reusability of this code (since it would be doubled).

@theofidry do you have any comments there or here?

/cc @mleczakm

@jdeniau
Copy link

jdeniau commented Aug 17, 2020 via email

@Zayon Zayon closed this by deleting the head repository Mar 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Multiple ObjectManagers with doctrine
6 participants