Как внедрить класс поставщика в сервис?

У меня есть пример службы:

<?php

namespace AppBundle\Service;

use AppBundle\Entity\Article;
use CompanyName\Utils\ClassFromVendor;

class DecorateArticle
{
    private $customDecorator;
    private $article;

    public function __construct(CustomDecorator $customDecorator)
    {
        $this->customDecorator = $customDecorator;
    }

    public function setNews(Article $article)
    {
        $this->article = $article;
    }

    public function decorate() : string
    {
         $text = strip_tags($this->article->getBody());

         $text = $this->customDecorator->doIt($text);

         $classFromVendor = new ClassFromVendor();
         $text = $classFromVendor->doIt($text);

         return $text;
    }
}

//controller:

public function showToApiAction(Request $request)
{
    $em = $this->getDoctrine()->getManager();
    $news = $em->getRepository('AppBundle:News')->find($request->get('id'));

    $decorateArticle = $this->get('AppBundle\Service\DecorateArticle');
    $decorateArticle->setNews($news);

    return $decorateArticle->decorate();
}

//services.yml:

AppBundle\Service\DecorateArticle:
    arguments:
        $decorateArticle: '@AppBundle\Service\DecorateArticle'
    public: true

В этом коде все работает хорошо, но мой руководитель группы сказал мне, что он нарушает принцип SOLID.

Вот почему я сделал:

<?php

namespace AppBundle\Service;

use AppBundle\Entity\Article;
use CompanyName\Utils\ClassFromVendor;

class DecorateArticle
{
    private $customDecorator;
    private $classFromVendor;
    private $article;

    public function __construct(CustomDecorator $customDecorator, ClassFromVendor $classFromVendor)
    {
        $this->customDecorator = $customDecorator;
        $this->classFromVendor = $classFromVendor;
    }

    public function setNews(Article $article)
    {
        $this->article = $article;
    }

    public function decorate() : string
    {
         $text = strip_tags($this->article->getBody());

         $text = $this->customDecorator->doIt($text);

         $text = $this->classFromVendor->doIt($text);

         return $text;
    }
}

//controller:

public function showToApiAction(Request $request)
{
    $em = $this->getDoctrine()->getManager();
    $news = $em->getRepository('AppBundle:News')->find($request->get('id'));

    $decorateArticle = $this->get('AppBundle\Service\DecorateArticle');
    $decorateArticle->setNews($news);

    return $decorateArticle->decorate();
}

//services.yml:

AppBundle\Service\DecorateArticle:
    arguments:
        $decorateArticle: '@AppBundle\Service\DecorateArticle'
        $classFromVendor: 'CompanyName\Utils\ClassFromVendor'
    public: true

Но эта ошибка броска:

Ошибка типа: Аргумент 2, переданный в AppBundle\Service\DecorateArticle::__construct(), должен быть экземпляром CompanyName\Utils\ClassFromVendor или нулевым, заданная строка

Как внедрить класс поставщика в сервис?

Может быть, есть лучший способ сделать то, что я делаю? Я не уверен, что смогу использовать инъекции в services.yml и сеттеры (setNews) в одном классе.


person mofem    schedule 27.03.2018    source источник
comment
Дело не в том, что у вас есть проблемы с инъекцией, а в том, что вы передаете строку вместо объекта. Проверьте свои аргументы   -  person Syntactic Fructose    schedule 27.03.2018
comment
Да, вы можете использовать инъекцию конструктора и установщика в одном и том же определении службы. Однако внимательно посмотрите на свои аргументы в определении службы. Заметили, что чего-то не хватает в classFromVendor? Вероятно, стоит просмотреть документацию по созданию сервисов, чтобы вы поняли, почему разница важна.   -  person Cerad    schedule 27.03.2018


Ответы (2)


попробуй это.

AppBundle\Service\DecorateArticle:
        arguments:
            decorateArticle: '@AppBundle\Service\DecorateArticle'
            classFromVendor: companyName.utils.ClassFromVendor
        public: true


companyName.utils.ClassFromVendor:
    class: CompanyName\Utils\ClassFromVendor
    public: false

Я не знаю, работает ли decorArticle, потому что вы используете в качестве параметра тот же сервис. Я думаю, что это правильный способ передать класс в качестве параметра сервису.

person Maxi Schvindt    schedule 28.03.2018
comment
Ну нет. Может попробовать еще раз. - person Cerad; 29.03.2018

В Symfony 4 мне нужно было внедрить класс поставщика в один из моих сервисов:

 jiraRestApi.ClassFromVendor:
    class: JiraRestApi\Issue\IssueService
    public: false

CM\Infrastructure\IssueJira\IssueJiraDbRepository:
    arguments: ['@jiraRestApi.ClassFromVendor', '@parameter_bag']
    public: false

Где jiraRestApi.ClassFromVendor, как вы могли подумать, является классом поставщика.

Мой IssueJiraDbRepository.php имеет конструктор:

public function __construct(JiraRestApi Issue, ParameterBagInterface $params){ ...

Итак, в вашем случае я предполагаю, что вам нужно сделать service.yaml:

CompanyName.ClassFromVendor:
    class: CompanyName\Utils\ClassFromVendor
    public: false

AppBundle\Service\DecorateArticle:
    arguments:['@CompanyName.ClassFromVendor']
    public: true

Помните о '@' в аргументе. Конструктор в контроллере должен остаться прежним:

public function __construct(CustomDecorator $customDecorator)
{
    $this->customDecorator = $customDecorator;
}

Рассматривайте services.yaml как конструктор для ваших контроллеров. Размещенные там аргументы будут использоваться для создания экземпляров контроллера. У вас есть ошибка, говорящая о том, что вы передали строку вместо объекта. Если у вас есть проблемы, вы всегда можете использовать автопрошивку, в большинстве случаев этого достаточно. Помните, что новые записи в service.yaml заменят предыдущие.

person GrzeGab    schedule 14.05.2019