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:
- Create a wrapper for the repository; this is faking the real injection (also called a decorator pattern).
- 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) - 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.