Symfony - 在服务中注入原则存储库
Symfony - inject doctrine repository in service
根据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'
但我遇到异常
Invalid service "acme.custom_repository": class
"EntityManager5aa02de170f88_546a8d27f194334ee012bfe64f629947b07e4919__CG__\Doctrine\ORM\EntityManager"
does not exist.
我如何在 Symfony 3.4 中执行此操作?
更新:
EntityClass 实际上是一个有效的 class FQCN(当然也使用了 phpstorm 上的复制参考),只是重命名了它,因为其中有一个公司名称 :)。无论如何更新它。
解决方案
BlueM 的 完美运行。
如果您不使用自动装配,这里是服务定义:
Acme\AcmeBundle\Respository\MyEntityRepository:
arguments:
- '@Doctrine\Common\Persistence\ManagerRegistry'
- Acme\AcmeBundle\Model\MyEntity # '%my_entity_class_parameter%'
检查参数是否有效 class(使用 FQCN 或使用 bundle 简化)例如:
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
希望对您有所帮助
正确创建自定义存储库
首先,您需要创建自定义存储库 class 以扩展来自 doctrine 的默认存储库:
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository
{
// your own methods
}
那么你需要在实体中添加这个注解 class:
/**
* @ORM\Entity(repositoryClass="MyDomain\Model\UserRepository")
*/
然后在 .yml 文件中定义存储库:
custom_repository:
class: MyDomain\Model\UserRepository
factory: ["@doctrine", getRepository]
arguments:
- Acme\FileBundle\Model\File
确保在您的存储库定义中 class
指向您的自定义存储库 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"
当您使用 Symfony 3.4 时,您可以使用更简单的方法,即 ServiceEntityRepository
。简单地实现你的存储库,让它 extend
class ServiceEntityRepository
然后你可以简单地注入它。 (至少在使用自动装配时——我没有将它与 classic 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 连接被重置,您将拥有对陈旧存储库的引用。但是恕我直言,这是我接受的风险,否则我将无法直接注入存储库..
到目前为止我在这里看到的解决方案还不错。我从另一个角度看它。所以我的解决方案允许您保持干净的存储库,强制执行一致的项目结构,并且您可以保持自动装配!
这就是我在 Symfony 5 中解决它的方法。
目标
我们希望拥有自动装配的存储库,并且我们希望让它们尽可能保持干净。我们也希望它们超级好用。
问题
我们需要找到一种方法来告诉 Repository 它应该使用的实体。
解决方案
解决方案很简单,包括以下几点:
- 我们有自定义存储库 class 扩展
Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository
class.
- 我们的习惯 class 上面有
public string $entity
属性。
当我们创建我们的新存储库并扩展我们的自定义存储库时 class 我们有两个选择:在我们的新存储库上我们可以像这样指向 class
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());
}
}
或者我们可以省略 属性 并让我们的新基础存储库 class 自动找到它! (稍后会详细介绍。)
代码
所以让我们从代码开始,然后我会解释它:
<?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'
然后在我们的新扩展 class 中,我知道默认在哪里寻找我的实体(这加强了一些一致性)。
非常重要的一点-我假设我们将使用完全相同的名称命名存储库和实体,例如:Post
将是我们的实体,PostRepository
是我们的存储库。请注意 Repository
这个词不是必须的。如果存在,它将被删除。
一些聪明的逻辑会为您创建名称空间 - 我假设您会遵循一些良好的做法,并且它们都是一致的。
完成!要让您的存储库自动装配,您需要做的就是扩展新的基本存储库 class 并将 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());
}
}
它干净、自动连线、创建起来超级简单和快速!
根据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'
但我遇到异常
Invalid service "acme.custom_repository": class "EntityManager5aa02de170f88_546a8d27f194334ee012bfe64f629947b07e4919__CG__\Doctrine\ORM\EntityManager" does not exist.
我如何在 Symfony 3.4 中执行此操作?
更新:
EntityClass 实际上是一个有效的 class FQCN(当然也使用了 phpstorm 上的复制参考),只是重命名了它,因为其中有一个公司名称 :)。无论如何更新它。
解决方案
BlueM 的
Acme\AcmeBundle\Respository\MyEntityRepository:
arguments:
- '@Doctrine\Common\Persistence\ManagerRegistry'
- Acme\AcmeBundle\Model\MyEntity # '%my_entity_class_parameter%'
检查参数是否有效 class(使用 FQCN 或使用 bundle 简化)例如:
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
希望对您有所帮助
正确创建自定义存储库
首先,您需要创建自定义存储库 class 以扩展来自 doctrine 的默认存储库:
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository
{
// your own methods
}
那么你需要在实体中添加这个注解 class:
/**
* @ORM\Entity(repositoryClass="MyDomain\Model\UserRepository")
*/
然后在 .yml 文件中定义存储库:
custom_repository:
class: MyDomain\Model\UserRepository
factory: ["@doctrine", getRepository]
arguments:
- Acme\FileBundle\Model\File
确保在您的存储库定义中 class
指向您的自定义存储库 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"
当您使用 Symfony 3.4 时,您可以使用更简单的方法,即 ServiceEntityRepository
。简单地实现你的存储库,让它 extend
class ServiceEntityRepository
然后你可以简单地注入它。 (至少在使用自动装配时——我没有将它与 classic 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 连接被重置,您将拥有对陈旧存储库的引用。但是恕我直言,这是我接受的风险,否则我将无法直接注入存储库..
到目前为止我在这里看到的解决方案还不错。我从另一个角度看它。所以我的解决方案允许您保持干净的存储库,强制执行一致的项目结构,并且您可以保持自动装配!
这就是我在 Symfony 5 中解决它的方法。
目标
我们希望拥有自动装配的存储库,并且我们希望让它们尽可能保持干净。我们也希望它们超级好用。
问题
我们需要找到一种方法来告诉 Repository 它应该使用的实体。
解决方案
解决方案很简单,包括以下几点:
- 我们有自定义存储库 class 扩展
Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository
class. - 我们的习惯 class 上面有
public string $entity
属性。 当我们创建我们的新存储库并扩展我们的自定义存储库时 class 我们有两个选择:在我们的新存储库上我们可以像这样指向 class
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()); } }
或者我们可以省略 属性 并让我们的新基础存储库 class 自动找到它! (稍后会详细介绍。)
代码
所以让我们从代码开始,然后我会解释它:
<?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'
然后在我们的新扩展 class 中,我知道默认在哪里寻找我的实体(这加强了一些一致性)。
非常重要的一点-我假设我们将使用完全相同的名称命名存储库和实体,例如:
Post
将是我们的实体,PostRepository
是我们的存储库。请注意Repository
这个词不是必须的。如果存在,它将被删除。一些聪明的逻辑会为您创建名称空间 - 我假设您会遵循一些良好的做法,并且它们都是一致的。
完成!要让您的存储库自动装配,您需要做的就是扩展新的基本存储库 class 并将 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()); } }
它干净、自动连线、创建起来超级简单和快速!