@denlem
Programmer

Можно ли в Symfony инициализировать в конструкторе сервисы если их не используешь?

Есть сервис в Symfony - SomeService (см. ниже)
И есть клиентский код который его использует (см. ниже)

В клиентском коде есть несколько разных вызовов разных методов сервиса SomeService и каждый метода может использовать общие сервисы на всех, а также и свой набор сервисов.

Соответственно проблема в том, что при вызове одного из методов сервиса, будет инициализация ненужных сервисов через конструктор сервиса, которые не используются в этих методах.

Как правильно решать проблему?

Делать общий родитель с общим конструктором и потом в классах потомках дополнять конструктор своими сервисами, используя конструкторы с вызовом внутри parent::__construct()? Решение возможно сомнительное - будет та же повторяемость кода(перечисление сервисов) в параметрах конструкторов потомков.

Или есть более правильное решение?

class SomeService
{
    private ServiceFirst  $serviceFirst;

    private ServiceSecond $serviceSecond;

    public function __construct(
           ServiceFirst $serviceFirst, 
           ServiceSecond $serviceSecond, 
           SomeCommonService $someCommonService
   )  {
        $this->serviceFirst = $serviceFirst;
        $this->serviceSecond = $serviceSecond;
        $this->someCommonService = $someCommonService;
    }
    
    public function methodFirst()
    {
        $this->someCommonService->doSomethingCommon();
        $this->serviceFirst->doSomethingFirst();

        return $this->commonMethod();
    }

    public function methodSecond()
    {
        $this->someCommonService->doSomethingCommon();
        $this->serviceSecond->doSomethingSecond();
        
        return $this->commonMethod();
    }

    private function commonMethod(): string
    {
        // some code
        
        return 'some string';
    }
}


И есть какой то клиентский код который его использует

// клиентский код1

$someResultString1 = $someService->methodFirst();
//...


// клиентский код2
$someResultString2 = $someService->methodSecond();
  • Вопрос задан
  • 135 просмотров
Решения вопроса 1
voronkovich
@voronkovich
Под ваше описание подходит Service Subscriber

Что такое "подписчик сервисов"?

Это сервис, который получает свои зависимости в виде локатора (https://en.wikipedia.org/wiki/Service_locator_pattern). При этом, локатор реализует ленивую загрузку, что позволяет не создавать экземпляры сервисов без необходимости.

Когда его следует использовать?

Когда у сервиса много зависимостей, но не все они используются им одновременно.
Примером, может служить AbstractController, который зависит от Twig, Doctrine, Router и т.д.
Очевидно, что не для каждого запроса нужен Twig (json api), и не в каждом запросе есть обращение к БД.
См. https://github.com/symfony/symfony/blob/e1581a0937...

Как его создать?

Реализовать интерфейс ServiceSubscriber:

<?php

use Psr\Container\ContainerInterface;
use Symfony\Contracts\Service\ServiceSubscriberInterface;

class SomeService 
{
    public function __constructor(
        private ContainerInterface $container,
    ) {
    }
    
    public static function getSubscribedServices(): array
    {
        return [
            ServiceFirst::class => ServiceFirst::class,
            ServiceSecond::class => ServiceSecond::class,
            SomeCommonService::class => SomeCommonService::class,
        ];
    }
    
    public function methodFirst()
    {
        $this->container[SomeCommonService::class]->doSomethingCommon();
        $this->container[ServiceFirst::class]->doSomethingFirst();

        return $this->commonMethod();
    }
    
    public function methodSecond()
    {
        $this->container[SomeCommonService::class]->doSomethingCommon();
        $this->container[ServiceSecond::class]->doSomethingSecond();
        
        return $this->commonMethod();
    }

    private function commonMethod(): string
    {
        // some code
        
        return 'some string';
    }
}


Однако, я соглашусь с ответами выше. В подавляющем большинстве случаев лучше будет разбить на несколько сервисов.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
Разбить по отдельным сервисам*, в том числе и содержимое commonMethod вынести в отдельную структуру, которую использовать в сервисах с methodFirst и methodSecond.

*это не обязательно должны быть сервисы
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы