I want to make a repository with hard dependencies. I found this blog post by Jurian Sluisman but he suggests getting the repository from the service manager and injecting it into the service where needed.
It would be much better if I would be able to get my custom repositories with injected dependencies like normally from my EntityManager
or ObjectManager
instance by using the getRepository
method:
$objectManager->getRepository('My\Entity\Class');
How can I use constructor injection in my Repositories and still get them like normally from the ObjectManager
directly with the getRepository
method?
Doctrine uses a factory class Doctrine\ORM\EntityManagerInterface\DefaultRepositoryFactory
for creating repository instances. If no custom factory is set this default factory is created here in the getRepositoryFactory
method in the Doctrine\ORM\Configuration
class.
By defining a custom repository_factory
we can overwrite this default factory class and add custom logic to the factory that will inject the hard dependencies:
To illustrate how you can do this I will show an example where the repository factory class creates repositories that are dependent on a ServiceLocator
instance through constructor injection.
1) make a custom factory class that implements the doctrine RepositoryFactory
interface
This class looks very similar to the doctrine DefaultRepositoryFactory
class.
<?php
namespace My\ORM\Repository;
use Doctrine\Common\Persistence\ObjectRepository;
use Doctrine\ORM\Repository\RepositoryFactory;
use Doctrine\ORM\EntityManagerInterface;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorAwareTrait;
use Zend\ServiceManager\ServiceLocatorInterface;
class CustomRepositoryFactory implements RepositoryFactory, ServiceLocatorAwareInterface
{
use ServiceLocatorAwareTrait;
/**
* @var ObjectRepository[]
*/
private $repositoryList = array();
/**
* @var ServiceLocator
*/
protected $serviceLocator;
/**
* @param ServiceLocatorInterface $serviceLocator
*/
public function __construct(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
}
/**
* {@inheritdoc}
*/
public function getRepository(EntityManagerInterface $entityManager, $entityName)
{
$repositoryHash = $entityManager->getClassMetadata($entityName)->getName() . spl_object_hash($entityManager);
if (isset($this->repositoryList[$repositoryHash])) {
return $this->repositoryList[$repositoryHash];
}
return $this->repositoryList[$repositoryHash] = $this->createRepository($entityManager, $entityName);
}
/**
* @param EntityManagerInterface $entityManager The EntityManager instance.
* @param string $entityName The name of the entity.
* @return ObjectRepository
*/
private function createRepository(EntityManagerInterface $entityManager, $entityName)
{
/* @var $metadata \Doctrine\ORM\Mapping\ClassMetadata */
$metadata = $entityManager->getClassMetadata($entityName);
$repositoryClassName = $metadata->customRepositoryClassName
?: $entityManager->getConfiguration()->getDefaultRepositoryClassName();
// Constructor injection, I check with subclass of but it is just an example
if(is_subclass_of($repositoryClassName, ServiceLocatorAwareInterface::class)){
$serviceLocator = $this->getServiceLocator()
$repository = new $repositoryClassName($entityManager, $metadata, $serviceLocator);
}else{
$repository = new $repositoryClassName($entityManager, $metadata);
}
return $repository;
}
}
2) Create a factory for the repository factory
<?php
namespace My\ORM\Repository\Factory;
use My\ORM\Repository\CustomRepositoryFactory;
use Zend\Cache\Storage\StorageInterface;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class CustomRepositoryFactoryFactory implements FactoryInterface
{
/**
* @param ServiceLocatorInterface $serviceLocator
* @return StorageInterface
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
return new CustomRepositoryFactory($serviceLocator);
}
}
3) register the factory for the repository factory in the service_manager
config
'service_manager' => array(
'factories' => array(
'My\ORM\Repository\CustomRepositoryFactory' => 'My\ORM\Repository\Factory\CustomRepositoryFactoryFactory'
)
)
4) register the repository factory in the doctrine config
'doctrine' => array(
'configuration' => array(
'orm_default' => array(
'repository_factory' => 'My\ORM\Repository\CustomRepositoryFactory'
)
)
)