Dependency injection in a Doctrine repository

Doctrine repositories are instantiated "magically": If you call $em->getRepository() you get a default repository class. You can create your own repository as well, but you never create that instance yourself. What if you need to inject a dependency into the repository? In this post I will explain how to achieve this with Zend Framework 2.

There are three ways to "inject" a dependency in a custom Doctrine repository:

  1. Create a wrapper for the repository; this is faking the real injection (also called a decorator pattern).
  2. Inject the dependency with a setter; this isn't using __construct() and will inject the dependency after the repository is instantiated (often used with soft dependencies)
  3. Inject the dependency via __construct() in your custom repository, as a hard dependency.

It is this third scenario which I will address here. In this example, we want to inject a Logger object which logs specific queries. (NB. I know this also works with Doctrine listeners, but just for the case of this example a Logger is used).

Furthermore, the example continues with an entity Foo which has a custom repository FooRepository. The repository extends from the default EntityRepository defined by Doctrine.

Constructor signature

A Doctrine repository requires the following constructor arguments:

public function __construct($em, Mapping\ClassMetadata $class)

If you need to extend this signature with your logger, you can redefine the constructor to suit your needs:

public function __construct($em, Mapping\ClassMetadata $class, Logger $logger)

Repository factory

This repository cannot be instantiated via the Doctrine entity manager anymore. Calling $em->getRepository('Foo'); will now raise a fatal error since the third paramter Logger is just null. Create a factory for the Zend Framework 2 service manager:

<?php

namespace MyModule\Factory\FooRepository;

use MyModule\Repository\FooRepository;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class FooRepositoryFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $sl)
    {
        $em     = $sl->get('Doctrine\ORM\EntityManager');
        $meta   = $em->getClassMetadata('Foo');
        $logger = $sl->get('MyLoggerService');

        $repository = new FooRepository($em, $meta, $logger);
        return $repository;
    }
}

Register this factory as the repository for your service:

'MyModule\Repository\FooRepository' => 'MyModule\Factory\FooRepositoryFactory'

Now if you need this repository in your service layer of controller, just inject the service MyModule\Repository\FooRepository and you have your custom Logger injected.