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

В общем у меня есть сервис Settings, в котором методы для получения настроек сайта.

Мне он нужен в каждом контроллере, ибо он используется в 99% контроллерах. Я решил просто сделать метод в родительском контроллере:

public function settingService()
{
    return $this->get('app.setting');
}


Но выпадает ошибки, в которой говорится, что контроллер знает только о "базовых" сервисах, якобы используйте DI.
Окей, допустим я сделаю в родительском:

abstract class Controller extends AbstractController
{
    public $settingService;

    public function __construct(Setting $settingService)
    {
        $this->settingService = $settingService;
    }
}


И это вроде здравое решение, но в таком случаи если я в дочернем контроллере будут использовать __construct, то мне нужно будет дублировать все аргументы конструктора. А если я захочу что-то добавить общее для всех контроллеров, то мне все контроллеры придётся править.

Есть идея как сделать лучше? А может я вообще не в ту сторону копаю...
  • Вопрос задан
  • 263 просмотра
Решения вопроса 1
BoShurik
@BoShurik Куратор тега Symfony
Symfony developer
Абстрактный контроллер реализует Service Subscriber. Вам достаточно туда добавить ваш Setting:

abstract class Controller extends AbstractController
{
    public static function getSubscribedServices()
    {
        return array_merge(parent::getSubscribedServices(), [
            'setting' => Setting::class,
        ]);
    }

    protected function getSetting(): Setting
    {
        return $this->get('setting');
    }
}
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
@Flying
Простейший вариант - написать свой compiler pass, в нём выбрать сервисы (через findTaggedServiceIds) по тегу controller.service_arguments и дополнить определение сервисов либо вызовом вашего метода либо добавлением аргумента в конструктор, это можно делать по имени.

Можно сделать ещё более общее решение: определить интерфейс, например SettingsAwareInterface примерно такого вида:
namespace App\Contracts;

interface SettingsAwareInterface
{
    public function getSettings(): Setting;

    public function setSettings(Setting $settings): void;
}

затем в services.yaml добавить:
_instanceof:
  App\Contracts\SettingsAwareInterface:
    tags:
      - {name: 'app.settings-aware'}

и в compiler pass работать с тегом app.settings-aware, это позволит вам передавать данный сервис не только в контроллеры.
Ответ написан
Maksclub
@Maksclub
maksfedorov.ru
будут использовать __construct, то мне нужно будет дублировать все аргументы конструктора

В каждом контроллере свои зависимости и это хорошо,
если что-то продублировалось — это случайность, ну точнее это не дубль в классическом понимании, тут логика не дублируется
Ответ написан
@AlpineMilk
Можно воспользоваться аннотацией @required
/**
     * @required
     */
    public function setSetting(Setting $setting)
    {
        $this->setting = $setting;
    }
Ответ написан
Комментировать
Ваш ответ на вопрос

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

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