Symfony - 在服务中注入学说存储库
Posted
技术标签:
【中文标题】Symfony - 在服务中注入学说存储库【英文标题】:Symfony - inject doctrine repository in service 【发布时间】:2018-08-15 23:17:18 【问题描述】:根据How to inject a repository into a service in Symfony2? 就像
acme.custom_repository:
class: Doctrine\ORM\EntityRepository
factory: ['@doctrine.orm.entity_manager', getRepository]
arguments:
- 'Acme\FileBundle\Model\File'
但我得到一个异常
无效的服务“acme.custom_repository”:类 “EntityManager5aa02de170f88_546a8d27f194334ee012bfe64f629947b07e4919__CG__\Doctrine\ORM\EntityManager” 不存在。
如何在 Symfony 3.4 中做到这一点?
更新:
EntityClass 实际上是一个有效的 FQCN 类(肯定也使用了 phpstorm 上的复制引用),只是将其重命名,因为其中包含公司名称 :)。无论如何更新它。
解决方案
BlueM 的solution 完美运行。 如果您不使用自动装配,这里是服务定义:
Acme\AcmeBundle\Respository\MyEntityRepository:
arguments:
- '@Doctrine\Common\Persistence\ManagerRegistry'
- Acme\AcmeBundle\Model\MyEntity # '%my_entity_class_parameter%'
【问题讨论】:
这可能会有所帮助:***.com/questions/44869590/… 很遗憾没有......仍然是同样的例外 【参考方案1】:到目前为止,我在这里看到的解决方案还不错。我从另一个角度看它。所以我的解决方案允许你保持干净的存储库,排序强制一致的项目结构,你可以继续自动装配!
这就是我在 Symfony 5 中解决它的方法。
目标
我们希望拥有自动装配的存储库,并且希望它们尽可能保持干净。我们还希望它们超级好用。
问题
我们需要想办法告诉 Repository 它应该使用的实体。
解决方案
解决方案很简单,包括几件事:
-
我们有自定义 Repository 类,它扩展了
Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository
类。
我们的自定义类有 public string $entity
属性。
当我们创建新的存储库并扩展我们的自定义存储库类时,我们有两个选择:在我们的新存储库上,我们可以像这样指向类
namespace App\Database\Repository\Post;
use App\Database\Repository\Repository;
use App\Entity\Blog\Post;
/**
* Class PostRepository
* @package App\Database\Repository
*/
class PostRepository extends Repository
public string $entity = Post::class;
public function test()
dd(99999, $this->getEntityName());
或者我们可以省略该属性,让我们的新基础 Repository 类自动找到它! (稍后会详细介绍。)
代码
那么让我们从代码开始,然后我会解释它:
<?php
namespace App\Database\Repository;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Laminas\Code\Reflection\ClassReflection;
use Symfony\Component\Finder\Finder;
/**
* Class Repository
* @package App\Database\Repository
*/
abstract class Repository extends ServiceEntityRepository
/** @var string */
private const REPOSITORY_FILE = 'repository';
/** @var string */
public string $entity = '';
/** @var string */
public string $defaultEntitiesLocation;
/** @var string */
public string $defaultEntitiesNamespace;
/**
* Repository constructor.
*
* @param ManagerRegistry $registry
* @param $defaultEntitiesLocation
* @param $defaultEntitiesNamespace
* @throws \Exception
*/
public function __construct(
ManagerRegistry $registry,
$defaultEntitiesLocation,
$defaultEntitiesNamespace
)
$this->defaultEntitiesLocation = $defaultEntitiesLocation;
$this->defaultEntitiesNamespace = $defaultEntitiesNamespace;
$this->findEntities();
parent::__construct($registry, $this->entity);
/**
* Find entities.
*
* @return bool
* @throws \ReflectionException
*/
public function findEntities()
if (class_exists($this->entity))
return true;
$repositoryReflection = (new ClassReflection($this));
$repositoryName = strtolower(preg_replace('/Repository/', '', $repositoryReflection->getShortName()));
$finder = new Finder();
if ($finder->files()->in($this->defaultEntitiesLocation)->hasResults())
foreach ($finder as $file)
if (strtolower($file->getFilenameWithoutExtension()) === $repositoryName)
if (!empty($this->entity))
throw new \Exception('Entity can\'t be matched automatically. It looks like there is' .
' more than one ' . $file->getFilenameWithoutExtension() . ' entity. Please use $entity
property on your repository to provide entity you want to use.');
$namespacePart = preg_replace(
'#' . $this->defaultEntitiesLocation . '#',
'',
$file->getPath() . '/' . $file->getFilenameWithoutExtension()
);
$this->entity = $this->defaultEntitiesNamespace . preg_replace('#/#', '\\', $namespacePart);
好的,那么这里发生了什么?我已经将一些值绑定到services.yml
中的容器:
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
bind:
$defaultEntitiesLocation: '%kernel.project_dir%/src/Entity'
$defaultEntitiesNamespace: 'App\Entity'
然后在我们的新扩展类中,我知道默认情况下在哪里查找我的实体(这会强制保持一定的一致性)。
非常重要的一点 - 我假设我们将使用完全相同的名称命名存储库和实体,例如:Post
将是我们的实体,PostRepository
是我们的存储库。请注意,Repository
这个词不是强制性的。如果它在那里,它将被删除。
一些聪明的逻辑会为你创建命名空间——我假设你会遵循一些好的实践并且它们都是一致的。
完成了!要让您的存储库自动装配,您需要做的就是扩展新的基本存储库类并将 Entity 命名为与存储库相同。所以最终结果如下所示:
<?php
namespace App\Database\Repository\Post;
use App\Database\Repository\Repository;
use App\Entity\Blog\Post;
/**
* Class PostRepository
* @package App\Database\Repository
*/
class PostRepository extends Repository
public function test()
dd(99999, $this->getEntityName());
它干净、自动连线、超级容易和快速创建!
【讨论】:
【参考方案2】:当您使用 Symfony 3.4 时,您可以使用更简单的方法,使用 ServiceEntityRepository
。只需实现您的存储库,让它extend
类ServiceEntityRepository
并且您可以简单地注入它。 (至少在使用自动装配时——我没有在经典的 DI 配置中使用它,但我认为它应该也可以工作。)
换句话说:
namespace App\Repository;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
class ExampleRepository extends ServiceEntityRepository
/**
* @param ManagerRegistry $managerRegistry
*/
public function __construct(ManagerRegistry $managerRegistry)
parent::__construct($managerRegistry, YourEntity::class);
现在,无需任何 DI 配置,您可以在任意位置注入存储库,包括控制器方法。
一个警告(这同样适用于您尝试注入存储库的方式):如果 Doctrine 连接被重置,您将获得对陈旧存储库的引用。但是恕我直言,这是我接受的风险,否则我将无法直接注入存储库..
【讨论】:
如果您碰巧使用多个实体管理器,您还需要小心一点。如果同一个实体类属于多个管理器,那么您无法预测最终会使用哪个管理器。 我现在如何使用存储库?你能扩展这个答案吗? 典型用法:构造函数注入。只需在代码中定义“public function __construct(ExampleRepository $repo) ”,无需配置即可解决依赖关系。【参考方案3】:正确创建自定义存储库
首先,您需要创建存储库自定义类,该类扩展自学说的默认存储库:
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository
// your own methods
那么在实体类中需要这个注解:
/**
* @ORM\Entity(repositoryClass="MyDomain\Model\UserRepository")
*/
然后在 .yml 文件中定义存储库:
custom_repository:
class: MyDomain\Model\UserRepository
factory: ["@doctrine", getRepository]
arguments:
- Acme\FileBundle\Model\File
确保在您的存储库的定义中class
指向您的自定义存储库类,而不是Doctrine\ORM\EntityRepository
。
将自定义服务注入您的自定义存储库:
在您的自定义存储库上为您的服务创建自定义设置器
使用 Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository
protected $paginator;
public function setPaginator(PaginatorInterface $paginator)
$this->paginator = $paginator;
然后像这样注入它们:
custom_repository:
class: MyDomain\Model\UserRepository
factory: ["@doctrine", getRepository]
arguments:
- Acme\FileBundle\Model\File
calls:
- [setPaginator, ['@knp_paginator']]
将您的存储库注入服务:
my_custom_service:
class: Acme\FileBundle\Services\CustomService
arguments:
- "@custom_repository"
【讨论】:
【参考方案4】:检查参数是一个有效的类(使用 FQCN 或使用捆绑简化)例如:
acme.custom_repository:
class: Doctrine\ORM\EntityRepository
factory:
- '@doctrine.orm.entity_manager'
- getRepository
arguments:
- Acme\MainBundle\Entity\MyEntity
或
acme.custom_repository:
class: Doctrine\ORM\EntityRepository
factory:
- '@doctrine.orm.entity_manager'
- getRepository
arguments:
- AcmeMainBundle:MyEntity
希望有帮助
【讨论】:
以上是关于Symfony - 在服务中注入学说存储库的主要内容,如果未能解决你的问题,请参考以下文章