У меня есть класс 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).
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'