Symfony - 在服务中注入doctrine存储库

时间:2018-03-07 18:30:49

标签: php symfony doctrine-orm symfony-3.4

根据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”:class   “EntityManager5aa02de170f88_546a8d27f194334ee012bfe64f629947b07e4919__CG __ \原则\ 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%'

4 个答案:

答案 0 :(得分:14)

当您使用Symfony 3.4时,您可以使用更简单的方法,使用ServiceEntityRepository。只需实现您的存储库,让它extendServiceEntityRepository,您只需注入它即可。 (至少在使用自动装配时 - 我没有使用经典的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连接,您将引用过时的存储库。但恕我直言,这是我接受的风险,否则我将无法直接注入存储库..

答案 1 :(得分:3)

检查参数是一个有效的类(使用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

希望这个帮助

答案 2 :(得分:1)

到目前为止,我在这里可以看到的解决方案还不错。我从另一个角度看了看它。因此,我的解决方案可让您保持干净的存储库,sorta强制执行一致的项目结构并保持自动装配!

这就是我在Symfony 5中解决它的方式。

目标

我们想要自动连接存储库,并希望它们保持尽可能干净。我们还希望它们超级易用。

问题

我们需要找出一种方法来告知存储库应使用的实体。

解决方案

该解决方案很简单,包括以下几项内容:

  1. 我们有自定义的Repository类,它扩展了Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository类。
  2. 我们的自定义类具有public string $entity属性。
  3. 在创建新存储库并扩展自定义存储库类时,我们有两种选择:在新存储库中,我们可以仅指向此类

    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'
  1. 然后在我们的新扩展类中,我知道默认情况下在哪里查找我的实体(这会保持一定的一致性)。

  2. 非常重要的位-我假设我们将使用完全相同的名称来命名存储库和实体,例如:Post将是我们的实体,PostRepository是我们的存储库。请注意,Repository一词不是必须的。如果有,它将被删除。

  3. 一些聪明的逻辑会为您创建名称空间-我假设您将遵循一些良好做法,并且它们将保持一致。

  4. 完成了!要使存储库自动连线,您需要做的就是扩展新的基本存储库类,并将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());
        }
    }
    

这是清洁,自动装配,超级容易且快速创建的!

答案 3 :(得分:0)

正确创建自定义存储库

首先,您需要创建从doctrine扩展默认存储库的存储库自定义类:

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"