Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[RFC] Review suggestions and approaches mentioned to override Doctrine mapping configuration #17646

Open
@mpdude

Description

@mpdude

I am faced with the question whether (and if so, how) it is possible to override the Doctrine ORM mapping configuration for entities provided in a bundle. In the most simple case, this means being able to re-configure e. g. the table or column names being used for an entity.

The documentation on this topic is sparse, slightly contradictory and recommendations possibly incomplete.

I would like to discuss our options here, get some guidance and be reminded of parts of the puzzle I probably miss. Once we get to conclusions, I'd be happy to prepare PR(s) with suggested updates.

Information, pointers and ideas from Symfony Documentation and related PRs/issues

The "Best Practicesfor Reusable Bundles" (emphasis mine) says:

If the bundle includes Doctrine ORM entities and/or ODM documents, it's recommended to define their mapping using XML files stored inResources/config/doctrine/. This allows to override that mapping using the standard Symfony mechanism to override bundle parts. This is not possible when using annotations/attributes to define the mapping.

Source:https://symfony.com/doc/current/bundles/best_practices.html#doctrine-entities-documents

Obviously, annotations/attributes live directly within the code and cannot easily be replaced from outside a bundle.

XML mapping configuration takes some extra effort (IMHO), but that would make sense if we get extra configuration flexibility from it.

But then, "the standard Symfony mechanism to override bundle parts" links to this:

Overriding entity mapping is only possible if a bundle provides a mapped superclass (such as the User entity in the FOSUserBundle). It's possible to override attributes and associations in this way. Learn more about this feature and its limitations in the Doctrine documentation.

Source:https://symfony.com/doc/current/bundles/override.html#entities-entity-mapping

That particular text was written in#14152. Before, that section was phrased as:

If a bundle defines its entity mapping in configuration files instead of annotations, you can override them as any other regular bundle configuration file. The only caveat is that you must override all those mapping configuration files and not just the ones you actually want to override.
If a bundle provides a mapped superclass [...] you can override ...

This previous version was written in#10053, based on the request in#7076.

It is not exactly correct, at least I don't see where and how DoctrineBundle would honor the override mechanism and prefer config files found through it. In other words, you cannot use a mechanismas there is for templates to drop in the replacement configuration.

The suggestion in#7076, however, was that it is indeed possible to overwrite thedoctrine.orm.mappings configuration for a particular bundlein its entirety to point to a different set of mapping configuration files. The OP in#7076 provides an example.

Using Compiler passes to set up mapping configuration

The reason why#14152 dismissed the suggestion from#7076 was

[...] works only becauseauto_mapping is set totrue [...]. From the POV of the bundle , it's IMHO not a good practice to rely on appauto_mapping configuration.

#14152 (comment)

It then goes on suggesting that bundles should use a dedicated compiler pass on their own (example given:https://github.com/FriendsOfSymfony/FOSUserBundle/blob/cf7fe27b2f4e1f298ee6eadf537267f8c9f9b85c/FOSUserBundle.php#L50) to set up their ORM mappings. This would make them completely independent ofdoctrine.orm.mappings settings.

Regarding the compiler pass, I was surprised to learn about it, since I have never seen it mentioned anywhere before. In fact, theRegisterMappingsPass base class is part of the Symfony Doctrine Bridge since v2.3 (symfony/symfony@099fd9f).

This compiler pass will hook up an extra XML, annotations or other Doctrine mapping driver in theMappingDriverChain,after the mapping drivers configured by thedoctrine.orm.mappings configuration. Since theMappingDriverChain works per namespace, such compiler passes (and compiler passes in general) win over configuration settings. This is pointed out correctly in#14152 (comment) as well; mapping configuration made by such a compiler pass cannot be overwritten by configuration, only byanother compiler pass that comes afterwards.

I agree that bundles should not rely onauto_mapping being set. I do not, however, share the assessment that#7076 only worked due toauto_mapping being used.

I think it is possible for a bundle toexplicitly register its mappingas ifauto_mapping were turned on, but without resorting to compiler passes. The solution is "prepending configuration", as outlined in the next section.

Alternative: Prepending Configuration

As an alternative to using the compiler pass, a bundle might also use thePrependExtensionInterface and the mechanism described inhttps://symfony.com/doc/current/bundles/prepend_extension.html#prepending-extension-in-the-bundle-class to provide configuration for DoctrineBundle.

To give an example, a bundle could have the following in its DependencyInjection/Extension class:

class ...implements PrependExtensionInterface{ ...publicfunctionprepend(ContainerBuilder$container)    {$container->prependExtensionConfig('doctrine', ['orm' => ['mappings' => ['MyBundleShortName' => ['dir' =>'Resources/config/doctrine',                    ],                ],            ],        ]);    }}

This explicitly provides the same configuration that would be created automatically ifauto_mapping were set totrue. Mapping configuration will be loaded from XML files inResources/config/doctrinerelative to the bundle class.

The application can, however, still overwrite this configuration:

# config.ymldoctrine:orm:# auto_mapping: false <-- irrelevantmappings:MyBundleShortName:is_bundle:false# <-- do not treat `dir` as relative to the bundletype:xmldir:"%kernel.project_dir%/src/Resources/MyBundle/config/doctrine"# whatever seems fitprefix:'MyBundle\Entity'# Namespace Prefix

The bottom line is that application configuration completely overwrites the default configuration provided by the bundle. There are no duplicate definitions competing in theMappingDriverChain; the configuration is merged before the mapping driver is configured.

Alternative: Intercepting Doctrine ORM events

You can intercept "metadata load" events in Doctrine ORM and use that to re-write/modify mapping configuration just in time.

Definetly not something I would recommend in the official documentation, and this puts metadata configuration in really unexpected places (even subscribers/listeners).

Just mentioned for completeness, since this is what search engine results brought up. Seems to work for some people.

Alternative: Mapped Superclasses

The docs mention "mapped superclasses" in case a bundle wants to support overriding attributes and associations. There is a reference to the limitations listed in the Doctrine documentation, but apart from that, it does not further elaborate that path.

In fact, this is very limited, since overrides can only be applied in entity classes that inherit from mapped superclasses, to re-configure parts of the mapping configuration inherited. It cannot be used together with entity inheritance hierachies.

Mapped superclasses mean that a bundle cannot work "out of the box", since it does not provide any ready-to-use entities. Users must completely subclass all those superclasses to create their own entities from it.

Additionally, the bundle needs to provide extra means to make the actual classes that shall be used configurable. For example, when code in the bundle wants to create a newUser entity, but that class is a mapped superclass, which class should be instantiated instead? How do we know about the appropriate constructor?

Also, when associations are defined between entities or superclasses, extra configuration is necessary so that Doctrine can know about the actual classes to be used at runtime.

Subclassing either entity or mapped superclasses for the sake of overriding mapping configuration through annotations or attributes requires fields to beprotected instead ofprivate. In that case, it leads to code duplication and/or inheritance-based designs only to create places where mapping configuration can be overwritten.

For these reasons, I think mapped superclasses are not a general solution to the problem. We should not suggest using them, at least not as the recommended way to make the mapping more configurable.

Side issue: Explicit naming configuration vs. Doctrine naming strategy

A side question I'd like to see answered is whether we should recommend bundle authors to avoid usingname="..." configurations for tables and columns (e. g. in@ORM\Column and@ORM\Table annotations).

Explicit configuration makes the database table and column names predictable, but might work against the particular Doctrine Naming Strategy chosen by the project/application. This may lead to inconsistent naming rules being used.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions


      [8]ページ先頭

      ©2009-2025 Movatter.jp