php 如何将存储库注入 Symfony 中的服务?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12223176/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 03:04:22  来源:igfitidea点击:

How to inject a repository into a service in Symfony?

phpsymfonydependency-injection

提问by ChocoDeveloper

I need to inject two objects into ImageService. One of them is an instance of Repository/ImageRepository, which I get like this:

我需要将两个对象注入ImageService. 其中之一是 的实例Repository/ImageRepository,我得到这样的:

$image_repository = $container->get('doctrine.odm.mongodb')
    ->getRepository('MycompanyMainBundle:Image');

So how do I declare that in my services.yml? Here is the service:

那么我如何在我的 services.yml 中声明呢?这是服务:

namespace Mycompany\MainBundle\Service\Image;

use Doctrine\ODM\MongoDB\DocumentRepository;

class ImageManager {
    private $manipulator;
    private $repository;

    public function __construct(ImageManipulatorInterface $manipulator, DocumentRepository $repository) {
        $this->manipulator = $manipulator;
        $this->repository = $repository;
    }

    public function findAll() {
        return $this->repository->findAll();
    }

    public function createThumbnail(ImageInterface $image) {
        return $this->manipulator->resize($image->source(), 300, 200);
    }
}

采纳答案by ChocoDeveloper

I found this linkand this worked for me:

我找到了这个链接,这对我有用:

parameters:
    image_repository.class:            Mycompany\MainBundle\Repository\ImageRepository
    image_repository.factory_argument: 'MycompanyMainBundle:Image'
    image_manager.class:               Mycompany\MainBundle\Service\Image\ImageManager
    image_manipulator.class:           Mycompany\MainBundle\Service\Image\ImageManipulator

services:
    image_manager:
        class: %image_manager.class%
        arguments:
          - @image_manipulator
          - @image_repository

    image_repository:
        class:           %image_repository.class%
        factory_service: doctrine.odm.mongodb
        factory_method:  getRepository
        arguments:
            - %image_repository.factory_argument%

    image_manipulator:
        class: %image_manipulator.class%

回答by Matthieu Napoli

Here is a cleaned up solution for those coming from Google like me:

对于像我这样来自谷歌的人来说,这是一个清理过的解决方案:

Update:here is the Symfony 2.6 (and up) solution:

更新:这是 Symfony 2.6(及更高版本)解决方案:

services:

    myrepository:
        class: Doctrine\ORM\EntityRepository
        factory: ["@doctrine.orm.entity_manager", getRepository]
        arguments:
            - MyBundle\Entity\MyClass

    myservice:
        class: MyBundle\Service\MyService
        arguments:
            - "@myrepository"


Deprecatedsolution (Symfony 2.5 and less):

已弃用的解决方案(Symfony 2.5 及更低版本):

services:

    myrepository:
        class: Doctrine\ORM\EntityRepository
        factory_service: doctrine.orm.entity_manager
        factory_method: getRepository
        arguments:
            - MyBundle\Entity\MyClass

    myservice:
        class: MyBundle\Service\MyService
        arguments:
            - "@myrepository"

回答by b.b3rn4rd

In case if do not want to define each repository as a service, starting from version 2.4you can do following, (defaultis a name of the entity manager):

如果不想将每个存储库定义为服务,从版本开始,2.4您可以执行以下操作,(default是实体管理器的名称):

@=service('doctrine.orm.default_entity_manager').getRepository('MycompanyMainBundle:Image')

回答by Tomá? Votruba

Symfony 3.3, 4 and 5 makes this much simpler.

Symfony 3.3、4 和 5 使这更简单。

Check my post How to use Repository with Doctrine as Service in Symfonyfor more general description.

查看我的文章How to use Repository with Doctrine as Service in Symfony以获得更一般的描述。

To your code, all you need to do is use composition over inheritance- one of SOLID patterns.

对于您的代码,您需要做的就是使用组合而不是继承- SOLID 模式之一。

1. Create own repository without direct dependency on Doctrine

1. 创建自己的仓库而不直接依赖 Doctrine

<?php

namespace MycompanyMainBundle\Repository;

use Doctrine\ORM\EntityManagerInterface;
use MycompanyMainBundle\Entity\Image;

class ImageRepository
{
    private $repository;

    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->repository = $entityManager->getRepository(Image::class);
    }

    // add desired methods here
    public function findAll()
    {
        return $this->repository->findAll();
    }
}

2. Add config registration with PSR-4 based autoregistration

2. 使用基于 PSR-4 的自动注册添加配置注册

# app/config/services.yml
services:
    _defaults:
        autowire: true

    MycompanyMainBundle\:
        resource: ../../src/MycompanyMainBundle

3. Now you can add any dependency anywhere via constructor injection

3. 现在你可以通过构造函数注入在任何地方添加任何依赖

use MycompanyMainBundle\Repository\ImageRepository;

class ImageService
{
    public function __construct(ImageRepository $imageRepository)
    {
        $this->imageRepository = $imageRepository;
    }
}

回答by Dimitrios Desyllas

In my case bases upon @Tomá? Votruba answer and this questionI propose the following approaches:

在我的情况下基于@Tomá?Votruba 的回答和这个问题我提出了以下方法:

Adapter Approach

适配器方法

Without Inheritance

无继承

  1. Create a generic Adapter Class:

    namespace AppBundle\Services;
    use Doctrine\ORM\EntityManagerInterface;
    
    class RepositoryServiceAdapter
    {
        private $repository=null;
    
        /**
        * @param EntityManagerInterface the Doctrine entity Manager
        * @param String $entityName The name of the entity that we will retrieve the repository
        */
        public function __construct(EntityManagerInterface $entityManager,$entityName)
        {
            $this->repository=$entityManager->getRepository($entityName)
        }
    
        public function __call($name,$arguments)
        {
          if(empty($arrguments)){ //No arguments has been passed
            $this->repository->$name();
          } else {
            //@todo: figure out how to pass the parameters
            $this->repository->$name(...$argument);
          }
        }
    }
    
  2. Then foreach entity Define a service, for examplein my case to define a (I use php to define symfony services):

     $container->register('ellakcy.db.contact_email',AppBundle\Services\Adapters\RepositoryServiceAdapter::class)
      ->serArguments([new Reference('doctrine'),AppBundle\Entity\ContactEmail::class]);
    
  1. 创建一个通用的适配器类:

    namespace AppBundle\Services;
    use Doctrine\ORM\EntityManagerInterface;
    
    class RepositoryServiceAdapter
    {
        private $repository=null;
    
        /**
        * @param EntityManagerInterface the Doctrine entity Manager
        * @param String $entityName The name of the entity that we will retrieve the repository
        */
        public function __construct(EntityManagerInterface $entityManager,$entityName)
        {
            $this->repository=$entityManager->getRepository($entityName)
        }
    
        public function __call($name,$arguments)
        {
          if(empty($arrguments)){ //No arguments has been passed
            $this->repository->$name();
          } else {
            //@todo: figure out how to pass the parameters
            $this->repository->$name(...$argument);
          }
        }
    }
    
  2. 然后foreach实体定义一个服务,例如在我的例子中定义一个(我用php来定义symfony服务):

     $container->register('ellakcy.db.contact_email',AppBundle\Services\Adapters\RepositoryServiceAdapter::class)
      ->serArguments([new Reference('doctrine'),AppBundle\Entity\ContactEmail::class]);
    

With Inheritance

有继承

  1. Same step 1 mentioned above

  2. Extend the RepositoryServiceAdapterclass for example:

    namespace AppBundle\Service\Adapters;
    
    use Doctrine\ORM\EntityManagerInterface;
    use AppBundle\Entity\ContactEmail;
    
    class ContactEmailRepositoryServiceAdapter extends RepositoryServiceAdapter
    {
      public function __construct(EntityManagerInterface $entityManager)
      {
        parent::__construct($entityManager,ContactEmail::class);
      }
    }
    
  3. Register service:

    $container->register('ellakcy.db.contact_email',AppBundle\Services\Adapters\RepositoryServiceAdapter::class)
      ->serArguments([new Reference('doctrine')]);
    
  1. 与上述相同的步骤 1

  2. 扩展RepositoryServiceAdapter类例如:

    namespace AppBundle\Service\Adapters;
    
    use Doctrine\ORM\EntityManagerInterface;
    use AppBundle\Entity\ContactEmail;
    
    class ContactEmailRepositoryServiceAdapter extends RepositoryServiceAdapter
    {
      public function __construct(EntityManagerInterface $entityManager)
      {
        parent::__construct($entityManager,ContactEmail::class);
      }
    }
    
  3. 注册服务:

    $container->register('ellakcy.db.contact_email',AppBundle\Services\Adapters\RepositoryServiceAdapter::class)
      ->serArguments([new Reference('doctrine')]);
    

Either the case you have a good testable way to function tests your database beavior also it aids you on mocking in case you want to unit test your service without the need to worry too much on how to do that. For example, let us suppose we have the following service:

如果您有一个很好的可测试方法来测试您的数据库行为,它也可以帮助您进行模拟,以防您想对您的服务进行单元测试,而无需过多担心如何去做。例如,假设我们有以下服务:

//Namespace definitions etc etc

class MyDummyService
{
  public function __construct(RepositoryServiceAdapter $adapter)
  {
    //Do stuff
  }
}

And the RepositoryServiceAdapter adapts the following repository:

并且 RepositoryServiceAdapter 适应以下存储库:

//Namespace definitions etc etc

class SomeRepository extends \Doctrine\ORM\EntityRepository
{
   public function search($params)
   {
     //Search Logic
   }
}

Testing

测试

So you can easily mock/hardcode/emulate the behavior of the method searchdefined in SomeRepositoryby mocking aither the RepositoryServiceAdapterin non-inheritance approach or the ContactEmailRepositoryServiceAdapterin the inheritance one.

所以,你可以很容易地模拟/硬编码/模拟方法的行为search中定义SomeRepository的嘲讽aither的RepositoryServiceAdapter非继承方法或ContactEmailRepositoryServiceAdapter在继承之一。

The Factory Approach

工厂方法

Alternatively you can define the following factory:

或者,您可以定义以下工厂:

namespace AppBundle\ServiceFactories;

use Doctrine\ORM\EntityManagerInterface;

class RepositoryFactory
{
  /**
  * @param EntityManagerInterface $entityManager The doctrine entity Manager
  * @param String $entityName The name of the entity
  * @return Class
  */
  public static function repositoryAsAService(EntityManagerInterface $entityManager,$entityName)
  {
    return $entityManager->getRepository($entityName);
  }
}

And then Switch to php service annotation by doing the following:

然后通过执行以下操作切换到 php 服务注释:

Place this into a file ./app/config/services.php(for symfony v3.4, .is assumed your ptoject's root)

把它放到一个文件中./app/config/services.php(对于 symfony v3.4,.假设你的 ptoject 的根)

use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
$definition = new Definition();

$definition->setAutowired(true)->setAutoconfigured(true)->setPublic(false);

// $this is a reference to the current loader
$this->registerClasses($definition, 'AppBundle\', '../../src/AppBundle/*', '../../src/AppBundle/{Entity,Repository,Tests,Interfaces,Services/Adapters/RepositoryServiceAdapter.php}');


$definition->addTag('controller.service_arguments');
$this->registerClasses($definition, 'AppBundle\Controller\', '../../src/AppBundle/Controller/*');

And cange the ./app/config/config.yml(.is assumed your ptoject's root)

并更改./app/config/config.yml(.假定您的 ptoject 的根)

imports:
    - { resource: parameters.yml }
    - { resource: security.yml }
    #Replace services.yml to services.php
    - { resource: services.php }

#Other Configuration

Then you can clace the service as follows (used from my example where I used a Dummy entity named Item):

然后,您可以按如下方式设置服务(从我的示例中使用,其中我使用了一个名为 的虚拟实体Item):

$container->register(ItemRepository::class,ItemRepository::class)
  ->setFactory([new Reference(RepositoryFactory::class),'repositoryAsAService'])
  ->setArguments(['$entityManager'=>new Reference('doctrine.orm.entity_manager'),'$entityName'=>Item::class]);

Also as a generic tip, switching to phpservice annotation allows you to do trouble-free more advanced service configuration thin one above. For code snippets use a special repositoryI made using the factorymethod.

同样作为一个通用提示,切换到php服务注释可以让您无故障地进行更高级的服务配置。对于代码片段,请使用我使用该方法制作的特殊存储库factory