Есть ли простой способ внедрить зависимость в каждый экземпляр репозитория в Doctrine2?
Я пытался прослушать событие loadClassMetadata и использовать инъекцию сеттера в хранилище, но это, естественно, привело к бесконечному циклу, поскольку вызов getRepository в событии вызвал то же самое событие.
Взглянув на метод Doctrine\ORM\EntityManager::getRepository, кажется, что репозитории вообще не используют внедрение зависимостей, вместо этого они создаются на уровне функции:
public function getRepository($entityName)
{
$entityName = ltrim($entityName, '\\');
if (isset($this->repositories[$entityName])) {
return $this->repositories[$entityName];
}
$metadata = $this->getClassMetadata($entityName);
$customRepositoryClassName = $metadata->customRepositoryClassName;
if ($customRepositoryClassName !== null) {
$repository = new $customRepositoryClassName($this, $metadata);
} else {
$repository = new EntityRepository($this, $metadata);
}
$this->repositories[$entityName] = $repository;
return $repository;
}
Если вы используете собственный EntityManager, вы можете переопределить метод getRepository. Поскольку это не связано с событием loadClassMetadata, вы не столкнетесь с бесконечным циклом.
Сначала вам нужно будет передать зависимость вашему пользовательскому EntityManager, а затем вы передадите ее объекту репозитория, используя инъекцию установщика.
Я ответил, как использовать собственный EntityManager здесь, но я воспроизведу ответ ниже:
1 - Переопределите параметр doctrine.orm.entity_manager.class, чтобы он указывал на ваш пользовательский менеджер сущностей (который должен расширять Doctrine\ORM\EntityManager).
2. Ваш пользовательский менеджер сущностей должен переопределить метод create, чтобы он возвращал экземпляр вашего класса. Посмотрите мой пример ниже и обратите внимание на последнюю строку, касающуюся MyEntityManager:
public static function create($conn, Configuration $config, EventManager $eventManager = null) {
if (!$config->getMetadataDriverImpl()) {
throw ORMException::missingMappingDriverImpl();
}
if (is_array($conn)) {
$conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, ($eventManager ? : new EventManager()));
} else if ($conn instanceof Connection) {
if ($eventManager !== null && $conn->getEventManager() !== $eventManager) {
throw ORMException::mismatchedEventManager();
}
} else {
throw new \InvalidArgumentException("Invalid argument: " . $conn);
}
// This is where you return an instance of your custom class!
return new MyEntityManager($conn, $config, $conn->getEventManager());
}
Вам также потребуется use следующее в своем классе:
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\ORMException;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Connection;
Изменить
Поскольку менеджер сущностей по умолчанию создается из метода create, вы не можете просто внедрить в него службу. Но поскольку вы создаете собственный менеджер сущностей, вы можете подключить его к контейнеру службы и внедрить любые необходимые вам зависимости.
Затем внутри переопределенного метода getRepository вы можете сделать что-то вроде $repository->setFoo($this->foo). Это очень простой пример - вы можете сначала проверить, есть ли у $repository метод setFoo, прежде чем вызывать его. Реализация зависит от вас, но это показывает, как использовать инъекцию сеттера для репозитория.
Ответ 2 · рейтинг 35 · Aldo Stracquadanio
Проблема в том, что классы репозитория не являются частью кодовой базы Symfony2, поскольку они являются частью Doctrine2, поэтому они не используют преимущества DIC; вот почему вы не можете делать инъекции в одном месте для всех репозиториев.
Я бы посоветовал вам использовать другой подход. Например, вы можете создать сервисный уровень поверх репозиториев и внедрить нужный вам класс через фабрику на этом уровне.
В противном случае вы также можете определить репозитории как сервисы следующим образом:
Решение, которое могло бы централизовать вызов метода set, заключается в написании тега DIC и передачи компилятора для его обработки и тегирования всех служб репозитория.
Ответ 3 · рейтинг 16 · samanime
Это YAML-версия ответа Альдо, на тот случай, если вы используете конфигурации YAML вместо XML.
Кроме того, следует отметить, что entity_manager_name является необязательным параметром. Мне нужно значение по умолчанию для моего конкретного использования, поэтому я просто оставил его пустым (на всякий случай, если я когда-нибудь переименую диспетчер по умолчанию).
Ответ 4 · рейтинг 2 · Tomas Votruba
Начиная с Symfony 3.3+ и 2017 вы можете пользоваться услугами.
Вместо других предлагаемых здесь решений, которые приводят к:
взлом фабрики репозиториев
создание конфигурации службы в YAML
и создание большого количества шаблонного кода, который позже выследит вас
Ты можешь это сделать...
Чистый путь - зависимость через внедрение конструктора, как и в любой другой службе
<?php declare(strict_types=1);
namespace App\Repository;
use App\Entity\Post;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
final class PostRepository
{
/**
* @var EntityRepository
*/
private $repository;
/**
* @var YourOwnDependency
*/
private $yourOwnDependency;
public function __construct(YourOwnDependency $YourOwnDependency, EntityManager $entityManager)
{
$this->repository = $entityManager->getRepository(Post::class);
$this->yourOwnDependency = $yourOwnDependency
}
}
Я только что определил свой собственный класс RepositoryFactory
Создайте класс RepositoryFactory и определите сервис, например my_service.orm_repository.robo_repository_factory, с инъекцией include @service_container
И добавьте службу проверки и установки контейнера, например:
public function process(ContainerBuilder $container)
{
$def = $container->getDefinition('doctrine.orm.configuration');
$def->addMethodCall(
'setRepositoryFactory', [new Reference('robo_doctrine.orm_repository.robo_repository_factory')]
);
}
После этого любой EntityRepository с ContainerAwareInterface имеет @service_container
Ответ 6 · рейтинг -2 · cadavre
На самом деле вы можете создать свой собственный DefaultRepository extends EntityRepository, построить его со всеми необходимыми зависимостями, а затем установить его как Repository по умолчанию с помощью: