Symfony 将依赖项注入实体存储库
有没有一种简单的方法可以将依赖项注入Doctrine2中的每个存储库实例 我曾尝试侦听Symfony 将依赖项注入实体存储库,symfony,doctrine-orm,Symfony,Doctrine Orm,有没有一种简单的方法可以将依赖项注入Doctrine2中的每个存储库实例 我曾尝试侦听loadClassMetadata事件并在存储库上使用setter注入,但这自然会导致无限循环,因为在事件中调用getRepository触发了相同的事件 在查看了条令\ORM\EntityManager::getRepository方法之后,似乎存储库根本没有使用依赖注入,而是在功能级别实例化: public function getRepository($entityName) { $entityN
loadClassMetadata
事件并在存储库上使用setter注入,但这自然会导致无限循环,因为在事件中调用getRepository
触发了相同的事件
在查看了条令\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,然后使用setter注入将其传递给存储库对象
我回答了如何使用自定义EntityManager,但我将复制以下答案:
1-覆盖条令.orm.entity\u manager.class
参数以指向您的自定义实体管理器(应扩展条令\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 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
方法。实现取决于您,但这展示了如何使用setter注入来实现存储库。问题是存储库类不是Symfony2代码库的一部分,因为它们是Doctrine2的一部分,所以它们不会利用DIC;这就是为什么不能在一个地方对所有存储库进行注入
我建议你使用不同的方法。例如,您可以在存储库的顶部创建一个服务层,并通过该层中的工厂实际注入所需的类
否则,您也可以通过以下方式将存储库定义为服务:
<service id="your_namespace.repository.repos_name"
class="%your_namespace.repository.repos_name%"
factory-service="doctrine" factory-method="getRepository">
<argument>entity_name</argument>
<argument>entity_manager_name</argument>
<call method="yourSetter">
<argument>your_argument</argument>
</call>
</service>
实体名称
实体经理姓名
你的论点
可以集中set方法调用的解决方案是编写一个DIC标记和一个编译器过程来处理它,并标记所有存储库服务。这是Aldo答案的YAML版本,以防您使用YAML配置而不是XML
您的_namespace.repository.repos_名称:
类:%u名称空间。存储库。repos\u名称%
工厂:[“@doctrine”,getRepository]
论据:
-实体名称
-实体经理姓名
电话:
-[setContainer,[“@service_container”]]
在版本2.8之前:
您的_namespace.repository.repos_名称:
类:%u名称空间。存储库。repos\u名称%
工厂服务:原则
工厂方法:getRepository
论据:
-实体名称
-实体经理姓名
电话:
-[setContainer,[@service_container]]
另外,请注意,实体\u管理器\u名称是一个可选参数。我想为我的特定用途使用默认值,所以我将其留空(以防我重命名默认管理器)。我刚刚定义了自己的RepositoryFactory类
my\u service.orm\u repository.robo\u repository\u factory
private function createRepository(EntityManagerInterface $entityManager, $entityName)
{
/* @var $metadata \Doctrine\ORM\Mapping\ClassMetadata */
$metadata = $entityManager->getClassMetadata($entityName);
$repositoryClassName = $metadata->customRepositoryClassName
?: $entityManager->getConfiguration()->getDefaultRepositoryClassName();
$result = new $repositoryClassName($entityManager, $metadata);
if ($result instanceof ContainerAwareInterface) {
$result->setContainer($this->container);
}
return $result;
}
public function process(ContainerBuilder $container)
{
$def = $container->getDefinition('doctrine.orm.configuration');
$def->addMethodCall(
'setRepositoryFactory', [new Reference('robo_doctrine.orm_repository.robo_repository_factory')]
);
}
EntityRepository
与containerWareInterface
具有@service\u container实际上,您可以创建自己的
DefaultRepository extensed EntityRepository
,使用所需的所有依赖项构建它,然后使用以下各项将其设置为默认存储库
:
doctrine:
orm:
entity_managers:
default:
default_repository_class: AppBundle\ORM\DefaultRepository
自Symfony 3.3+和2017年以来您可以使用服务
而不是此处提出的导致以下结果的其他解决方案:
- 黑客入侵存储库工厂
- 在YAML中进行服务配置
- 并创建了大量的样板代码,稍后将找到您
干净方式-通过构造函数注入的依赖性,就像在任何其他服务中一样
投票赞成这个很好的答案,但我选择了Arms答案,因为我发现它更容易实现。这是一个很棒的答案。我依靠的是一种温和的黑客方法,它在2.3版中停止了工作。我改用这种方法,效果很好。我在下面发布了一个相同的YAML格式,仅供参考。如何修改doctor.orm.entity\u manager.class服务来添加setter注入?我已经在vendor/symfony/src/symfony/Bundle/DoctrineBundle/Resources/config/orm.xml中查看了定义,但是我对如何添加注入有些困惑,因为我正在使用yml来定义我的服务我已经正确地设置了所有内容,但是当你说“您可以将其连接到服务容器并注入所需的任何依赖项”我在自定义EntityManager上有一个setContainer方法,我不知道使用什么配置来“连接”。我还对最后一个“连接到服务容器”步骤感到困惑。我也不确定如何“将其连接到服务容器”“-其他一切看起来都很好
<?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
}
}