Symfony2: как получить доступ к сервису из репозитория

У меня есть класс ModelsRepository:

class ModelsRepository extends EntityRepository {} 

И обслуживание

 container_data: class: ProjectName\MyBundle\Common\Container arguments: [@service_container] 

Я хочу получить доступ из ModelsRepository для обслуживания container_data. Я не могу передать службу из конструктора, используемого контроллером.

Вы знаете, как это сделать?

IMHO, это не должно быть необходимо, так как вы можете легко нарушать правила, такие как SRP и Law of Demeter

Но если вам это действительно нужно, вот как это сделать:

Во-первых, мы определяем базовый класс «ContainerAwareRepository», который имеет вызов «setContainer»,

services.yml

 services: # This is the base class for any repository which need to access container acme_bundle.repository.container_aware: class: AcmeBundle\Repository\ContainerAwareRepository abstract: true calls: - [ setContainer, [ @service_container ] ] 

ContainerAwareRepository может выглядеть так:

AcmeBundle \ Repository \ ContainerAwareRepository.php

 abstract class ContainerAwareRepository extends EntityRepository { protected $container; public function setContainer(ContainerInterface $container) { $this->container = $container; } } 

Затем мы можем определить наш репозиторий моделей.
Мы используем здесь метод доктрины getRepository , чтобы построить наш репозиторий

services.yml

 services: acme_bundle.models.repository: class: AcmeBundle\Repository\ModelsRepository factory_service: doctrine.orm.entity_manager factory_method: getRepository arguments: - "AcmeBundle:Models" parent: acme_bundle.repository.container_aware 

И тогда просто определите класс

AcmeBundle \ Repository \ ModelsRepository.php

 class ModelsRepository extends ContainerAwareRepository { public function findFoo() { $this->container->get('fooservice'); } } 

Чтобы использовать репозиторий, вам необходимо сначала вызвать его из службы.

 $container->get('acme_bundle.models.repository')->findFoo(); // No errors $em->getRepository('AcmeBundle:Models')->findFoo(); // No errors 

Но если вы прямо

 $em->getRepository('AcmeBundle:Models')->findFoo(); // Fatal error, container is undefined 

Вы никогда не должны передавать контейнер в репозиторий, так же, как вы никогда не должны позволять сущности обрабатывать тяжелую логику. У репозиториев есть только одна цель – получение данных из базы данных. Ничего больше (читайте: http://docs.doctrine-project.org/en/2.0.x/reference/working-with-objects.html ).

Если вам нужно что-то более сложное, вы, вероятно, должны создать для этого отдельный (контейнер, если хотите).

Я попробовал несколько версий. Проблема была решена следующим образом

ModelRepository:

 class ModelRepository extends EntityRepository { private $container; function __construct($container, $em) { $class = new ClassMetadata('ProjectName\MyBundle\Entity\ModelEntity'); $this->container = $container; parent::__construct($em, $class); } } 

security.yml:

 providers: default: id: model_auth 

services.yml

 model_auth: class: ProjectName\MyBundle\Repository\ModelRepository argument 

В результате я получил репозиторий с контейнером использования возможностей – по мере необходимости. Но эта реализация может использоваться только в критических случаях, потому что у нее есть ограничения для Репозитория. Thx 4all.

Вы уверены, что это хорошая идея получить доступ к сервису из репо?

Репозитории предназначены для пользовательского SQL, где, в случае доктрины, доктрина может помочь вам с findOne() find() , findOne() , findBy() , […] «магии».

Учтите, чтобы внедрить вашу службу, когда вы используете свое репо, и, если вам нужны какие-то параметры, передайте его непосредственно методу репо.

Я бы предложил использовать заводскую услугу:

http://symfony.com/doc/current/components/dependency_injection/factories.html

 //Repository class ModelsRepositoryFactory { public static function getRepository($entityManager,$entityName,$fooservice) { $em = $entityManager; $meta = $em->getClassMetadata($entityName); $repository = new ModelsRepository($em, $meta, $fooservice); return $repository; } } //service AcmeBundle.ModelsRepository: class: Doctrine\ORM\EntityRepository factory: [AcmeBundle\Repositories\ModelsRepositoryFactory,getRepository] arguments: - @doctrine.orm.entity_manager - AcmeBundle\Entity\Models - @fooservice 

Расширение Laurynas Mališauskas отвечает, чтобы передать сервис конструктору, сделать ваш репозиторий тоже сервисом и передать его аргументами:

 models.repository: class: ModelsRepository arguments: ['@service_you_want_to_pass'] 

Самый простой способ – включить службу в конструктор репозитория.

 class ModelsRepository extends EntityRepository { private $your_service; public function __construct(ProjectName\MyBundle\Common\Container $service) { $this->your_service = $service; } } 

Я категорически согласен с тем, что это нужно делать только в случае крайней необходимости. Хотя сейчас существует более простой подход (проверенный с помощью Symfony 2.8).

  1. Реализовать в вашем репозитории «ContainerAwareInterface»
  2. Используйте «ContainerAwareTrait»
  3. настройте services.yml

RepositoryClass:

 namespace AcmeBundle\Repository; use Doctrine\ORM\EntityRepository; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; use AcmeBundle\Entity\User; class UserRepository extends EntityRepository implements ContainerAwareInterface { use ContainerAwareTrait; public function findUserBySomething($param) { $service = $this->container->get('my.other.service'); } } 

services.yml:

 acme_bundle.repository.user: lazy: true class: AcmeBundle\Repository\UserRepository factory: ['@doctrine.orm.entity_manager', getRepository] arguments: - "AcmeBundle:Entity/User" calls: - method: setContainer arguments: - '@service_container'