简体   繁体   English

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

[英]How to inject a repository into a service in Symfony?

I need to inject two objects into ImageService .我需要将两个对象注入ImageService One of them is an instance of Repository/ImageRepository , which I get like this:其中之一是Repository/ImageRepository的实例,我得到这样的:

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

So how do I declare that in my services.yml?那么我如何在我的 services.yml 中声明呢? Here is the service:这是服务:

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);
    }
}

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"

Deprecated solution (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"

I found this link and 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%

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

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

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 Symfony for 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;
    }
}

In my case bases upon @Tomáš Votruba answer and this question I 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):然后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与上述相同的步骤 1

  2. Extend the RepositoryServiceAdapter class for example:扩展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. Register service:注册服务:

     $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 search defined in SomeRepository by mocking aither the RepositoryServiceAdapter in non-inheritance approach or the ContactEmailRepositoryServiceAdapter in the inheritance one.因此,您可以通过模拟非继承方法中的RepositoryServiceAdapter或继承方法中的ContactEmailRepositoryServiceAdapter来轻松模拟/硬编码/模拟SomeRepository定义的方法search的行为。

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 (为symfony1.2 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 php service annotation allows you to do trouble-free more advanced service configuration thin one above.同样作为一个通用提示,切换到php服务注释可以让您无障碍地进行更高级的服务配置。 For code snippets use a special repository I made using the factory method.对于代码片段,请使用我使用factory方法制作的特殊存储库

For Symfony 5 it is really simple, without need of services.yml to inject the dependency:对于 Symfony 5,它真的很简单,不需要 services.yml 来注入依赖:

  1. inject the Entity Manager in the service constructor在服务构造函数中注入实体管理器
private $em; public function __construct(EntityManagerInterface $em) { $this->em = $em; }
  1. Then get the repository :然后获取存储库:

$this->em->getRepository(ClassName::class) $this->em->getRepository(ClassName::class)

by replacing ClassName with your entity name.通过将 ClassName 替换为您的实体名称。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM