Symfony의 서비스에 저장소를 삽입하는 방법은 무엇입니까?


78

두 개의 개체를 ImageService. 그중 하나는의 인스턴스이며 Repository/ImageRepository다음과 같이 표시됩니다.

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

그렇다면 내 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);
    }
}


@simshaun 감사합니다. yml
ChocoDeveloper

답변:


105

다음은 저와 같은 Google 사용자를위한 정리 된 솔루션입니다.

업데이트 : 다음은 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"

더 이상 사용되지 않는 솔루션 (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"

2
MongoDB를 사용하는 동안 doctrine.odm.mongodb.document_managerfactory_service로 사용
Pratyush

이것은 훌륭하게 작동하지만 이러한 방식으로 추가하는 모든 리포지토리를 $this->get('myrepository'). 리포지토리를 myservice서비스 자체로 정의하지 않고 인수로 정의 / 전달하는 방법이 있습니까?
Andy

1
당신은 서비스를 정의 할 수 있습니다 @Andy private가 (YAML의 설정에) 주입하지만 사용하여 인출 할 수있는 수단->get()
마티유 나폴리를

2
사용 중단 경고 : 더 이상 factory_servicefactory_method이후 심포니 2.6 . 이것이 지금 수행되어야하는 방법입니다 : stackoverflow.com/a/31807608/828366
Francesco Casula

1
Symfony 3.0 이후 로 일부 YAML 구성 에는 따옴표를 사용해야합니다 . 따라서 여기서를 사용해야합니다 factory: ["@doctrine.orm.entity_manager", getRepository]. 그렇지 않으면 예쁜 ParseException이 맞을 것입니다.
Czechnology

45

링크를 찾았고 이것은 나를 위해 일했습니다.

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%

6
DEPRECATION WARNING : Symfony 2.6 이후로 더 이상 factory_service 및 factory_method 없음
luchaninov

기본 팩토리는 없지만 Symfony 3.4는 자체 팩토리를 만드는 방법을 지원합니다.
Dimitrios Desyllas 2010

40

각 저장소를 서비스로 정의하지 않으려는 경우 버전부터 2.4다음을 수행 할 수 있습니다. ( default은 엔티티 관리자의 이름입니다.)

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

3
이것이 XML 서비스 파일에서 어떻게 보일까요?
Jonny

1
이것은 표현식 구성 요소를 기반으로합니다 : symfony.com/doc/current/book/…
HenningCash

6
Symfony 2.7을 사용하여 더 짧은 구문으로 저장소를 얻을 수있었습니다.@=service('doctrine').getRepository('AppBundle:EntityX')
mgalic

이것은 완벽로 번역된다 "$ this->의 get ("교리 ") -> getRepository : * Container.php에서 ("AppBundle EntityX은 ")" "이 바로 가기를 사랑 해요!
토마스 Decaux

@Jonny 여기 xml 버전이 있습니다 :<service id="image_manager" class="MyCompany\MainBundle\ImageManager"> <argument type="expression">service('doctrine.orm.default_entity_manager').getRepository('MycompanyMainBundle:Image')</argument> </service>
Emilie

17

Symfony 3.3, 4 및 5는이를 훨씬 더 간단하게 만듭니다.

더 일반적인 설명 내 게시물 How to use Repository with Doctrine as Service in Symfony확인하십시오 .

코드에서해야 할 일은 SOLID 패턴 중 하나 인 상속보다 합성을 사용하는 것입니다 .

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. PSR-4 기반 자동 등록으로 구성 등록 추가

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

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

3. 이제 생성자 주입을 통해 모든 종속성을 추가 할 수 있습니다.

use MycompanyMainBundle\Repository\ImageRepository;

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

Symfony 4.1에 대한 최신 버전입니까?
Isengo

예, 시공 주입 역학은 Symfony 5까지 변경해서는 안됩니다. 어떤 문제가 있습니까?
Tomáš Votruba

나는 UserManager라는 서비스 폴더에 서비스를 생성했고 내 UsersRepository를 사용하고 싶습니다. "class UsersRepository가 ServiceEntityRepository를 확장합니다"
Isengo

그것은 내가 게시물에서 옹호하는 다른 접근 방식입니다. Symfony 및 Doctrine에 대한 거의 모든 데이터베이스 관련 항목 서비스를 한 번에 막대한 공급 업체 잠금으로 만듭니다. 자세한 내용은 게시물을 참조하십시오
Tomáš Votruba 2010 년

0

토마스 Votruba 응답이 @에 따라 내 경우 기지에서 질문 나는 다음과 같은 방법을 제안한다 :

어댑터 접근

상속없이

  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를 사용합니다).

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

상속으로

  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')]);
    

어떤 경우 든 데이터베이스 동작을 테스트 할 수있는 좋은 방법이있는 경우에도이를 수행하는 방법에 대해 너무 걱정할 필요없이 서비스를 단위 테스트하려는 경우 조롱하는 데 도움이됩니다. 예를 들어 다음과 같은 서비스가 있다고 가정 해 보겠습니다.

//Namespace definitions etc etc

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

그리고 RepositoryServiceAdapter는 다음 저장소를 조정합니다.

//Namespace definitions etc etc

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

테스팅

따라서 비상 속 방식 또는 상속 방식 중 하나 를 조롱하여 에서 search정의 된 메서드의 동작을 쉽게 모의 / 하드 코딩 / 에뮬레이션 할 수 있습니다 .SomeRepositoryRepositoryServiceAdapterContactEmailRepositoryServiceAdapter

공장 접근법

또는 다음 팩토리를 정의 할 수 있습니다.

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

그런 다음 다음을 수행하여 PHP 서비스 주석으로 전환하십시오.

이것을 파일에 넣으십시오 ./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/*');

그리고 cange ./app/config/config.yml( .당신의 ptoject의 뿌리로 간주됩니다)

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

#Other Configuration

그런 다음 서비스를 다음과 같이 처리 할 수 ​​있습니다 (예에서라는 이름의 더미 엔티티를 사용한 예에서 사용됨 Item).

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

또한 일반적인 팁으로, php서비스 주석으로 전환하면 위의 얇은 서비스 구성에 문제가없는 고급 서비스 구성을 수행 할 수 있습니다. 코드 조각의 경우 메서드를 사용하여 만든 특수 저장소를 사용합니다 factory.


그것을 제안 하는지 설명해 주 시겠습니까? 원시 솔루션과 비교할 때 IDE의 자동 완성 도움말을 잃어 버리고 무엇을 얻습니까?
Nico Haase
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.