doctrine / orm

Doctrine Object Relational Mapper (ORM)
https://www.doctrine-project.org/projects/orm.html
MIT License
9.86k stars 2.5k forks source link

Cannot override the class metadata factory via configuration since 3.0 #11496

Open alekitto opened 2 weeks ago

alekitto commented 2 weeks ago

Bug Report

Q A
BC Break no (bug introduced in major)
Version 3.0.0

Summary

Since version 3.0.0, the class metadata factory cannot be overridden.

Previous behavior

The entity manager has been instantiated taking the class name from the configuration on entity manager construction

Current behavior

Since https://github.com/doctrine/orm/commit/ea97ea4c470f81f7944e4c8931f04c17eb86bee7 the override is broken as the entity manager is wired to the class Doctrine\ORM\Mapping\ClassMetadataFactory. To allow overriding via configuration, it should be wired against Doctrine\Persistence\Mapping\ClassMetadataFactory interface.

How to reproduce

$conn = new Connection(...);

$config = new Configuration();
$config->setClassMetadataFactoryName(FakeMetadataFactory::class);

new EntityManager($conn, $config);

This is the same exact bug reported on mongodb-odm at https://github.com/doctrine/mongodb-odm/issues/2551 The only difference is that on mongodb-odm this was considered a BC break, as the bug was introduced in a minor version.

derrabus commented 1 week ago

Obviously not a bug. Please elaborate your use-case.

alekitto commented 1 week ago

As explained also in the ODM issue:

It is mostly used for testing actually: I'm using a DocumentManager with a mocked connection and a custom metadata factory in order to precisely control what is returned from the factory itself

I used this feature in 2.x without any problem, but defining the concrete type into the EntityManager does not allow to use any other class implementing the common interface/extending the abstract class.

derrabus commented 1 week ago

You did not answer the question.

alekitto commented 1 week ago

You did not answer the question.

Yes, I did. My use-case is to create a fake/mock version of the class metadata factory implementing Doctrine\Persistence\Mapping\ClassMetadataFactory interface, and using it in testing to control what is returned from the factory itself and when the factory methods are called.

In the current state calling setClassMetadataFactoryName on configuration with such a class results in a fatal error on entity manager construction. But the same thing was possible in 2.x because the property type was not defined, so it was possible to implement the interface/extend the abstract class to have your custom implementation of the class metadata factory.

What else do you need to know precisely?

derrabus commented 1 week ago

As far as I know, our factory is not final, so you may still mock it.

alekitto commented 1 week ago

The class itself is not final, but some of the public methods are final in ClassMetadataFactory and in AbstractClassMetadataFactory and cannot be overridden (also what's the purpose of the abstract class if cannot be used in the entity manager?) .

I'm currently replacing the class completely, just implementing the interface.

derrabus commented 1 week ago

I see. Well, we could introduce a new interface for our factory that you can mock. Please send a PR.

alekitto commented 1 week ago

:+1: Ok, thanks. I'll send it tomorrow.