Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to override bundled Doctrine repository in Symfony

I have an independent Symfony bundle (installed with Composer) with entities and repositories to share between my applications that connect same database.

Entities are attached to every applications using configuration (yml shown):

doctrine:
    orm:
        mappings:
            acme:
                type: annotation
                dir: %kernel.root_dir%/../vendor/acme/entities/src/Entities
                prefix: Acme\Entities
                alias: Acme

Well, it was the easiest way to include external entities in application, but looks a bit ugly.

Whenever I get repository from entity manager:

$entityManager->getRepository('Acme:User');

I get either preconfigured repository (in entity configuration) or default Doctrine\ORM\EntityRepository.

Now I want to override bundled (or default) repository class for a single entity. Is there any chance to do it with some configuration/extension/etc?

I think, the best looking way is something like:

doctrine:
    orm:
         ....:
             Acme\Entities\User:
                 repositoryClass: My\Super\Repository

Or with tags:

my.super.repository:
    class: My\Super\Repository
    tags:
        - { name: doctrine.custom.repository, entity: Acme\Entities\User }
like image 914
Ostin Avatar asked Mar 11 '16 14:03

Ostin


People also ask

How to override bundle Symfony?

The easiest way to "override" a bundle's routing is to never import it at all. Instead of importing a third-party bundle's routing, copy that routing file into your application, modify it, and import it instead.

What is a bundle in Symfony?

A Symfony bundle is a collection of files and folders organized in a specific structure. The bundles are modeled in such a way that it can be reused in multiple applications. The main application itself is packaged as a bundle and it is generally called AppBundle.


2 Answers

You can use LoadClassMetadata event:

class LoadClassMetadataSubscriber implements EventSubscriber
{

    /**
     * @inheritdoc
     */
    public function getSubscribedEvents()
    {
        return [
            Events::loadClassMetadata
        ];
    }

    /**
     * @param LoadClassMetadataEventArgs $eventArgs
     */
    public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
    {
        /**
         * @var \Doctrine\ORM\Mapping\ClassMetadata $classMetadata
         */
        $classMetadata = $eventArgs->getClassMetadata();

        if ($classMetadata->getName() !== 'Acme\Entities\User') {
            return;
        }

        $classMetadata->customRepositoryClassName = 'My\Super\Repository';
    }

}

Doctrine Events

Entities are attached to every applications using configuration (yml shown): Well, it was the easiest way to include external entities in application, but looks a bit ugly.

You can enable auto_mapping

like image 181
Arthur Avatar answered Nov 08 '22 19:11

Arthur


Works for Doctrine versions <2.5

In addition to Artur Vesker answer I've found another way: override global repository_factory.

config.yml:

doctrine:
    orm:
        repository_factory: new.doctrine.repository_factory

services.yml:

new.doctrine.repository_factory:
    class: My\Super\RepositoryFactory

Repository Factory:

namespace My\Super;

use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Repository\DefaultRepositoryFactory;

class RepositoryFactory extends DefaultRepositoryFactory
{
    /**
     * @inheritdoc
     */
    protected function createRepository(EntityManagerInterface $entityManager, $entityName)
    {
        if ($entityName === Acme\Entities\User::class) {
            $metadata = $entityManager->getClassMetadata($entityName);
            return new ApplicationRepository($entityManager, $metadata);
        }

        return parent::createRepository($entityManager, $entityName);
    }
}

No doubt implementing LoadClassMetadataSubscriber is a better way.

like image 41
Ostin Avatar answered Nov 08 '22 20:11

Ostin