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

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

<?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 или иметь значение null, заданную строку

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

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

1 ответ

В 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 как конструктор для ваших контроллеров. Размещенные здесь аргументы будут использоваться для создания экземпляров контроллера. Ошибка заключается в том, что вы передали строку вместо объекта. Если у вас есть проблемы, вы всегда можете использовать autowire, в большинстве случаев этого достаточно. Помните, что новые записи в service.yaml заменят предыдущие.

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

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


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

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

Другие вопросы по тегам