A
A
Alexander2021-08-11 10:57:15
symfony
Alexander, 2021-08-11 10:57:15

How to make a shared service in all controllers?

In general, I have a Settings service, in which methods for getting site settings.

I need it in every controller, because it is used in 99% of controllers. I decided to just make a method in the parent controller:

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


But an error is thrown saying that the controller only knows about "basic" services, supposedly use DI.
Okay, let's say I do in the parent:

abstract class Controller extends AbstractController
{
    public $settingService;

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


And this seems like a sound solution, but in such cases, if I use __construct in the child controller, then I will need to duplicate all the constructor arguments. And if I want to add something common to all controllers, then I will have to edit all the controllers.

Have an idea how to do it better? Or maybe I'm digging in the wrong direction...

Answer the question

In order to leave comments, you need to log in

4 answer(s)
B
BoShurik, 2021-08-11
@alexrakirov

The abstract controller implements the Service Subscriber . You just need to add your Setting there:

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');
    }
}

F
Flying, 2021-08-11
@Flying

The simplest option is to write your own compiler pass , select services in it (via findTaggedServiceIds) by tag controller.service_argumentsand complete the definition of services either by calling your method or adding an argument to the constructor, this can be done by name.
You can make an even more general solution: define an interface, for example SettingsAwareInterfacesomething like this:

namespace App\Contracts;

interface SettingsAwareInterface
{
    public function getSettings(): Setting;

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

then in services.yamladd:
_instanceof:
  App\Contracts\SettingsAwareInterface:
    tags:
      - {name: 'app.settings-aware'}

and in the compiler pass to work with the tag app.settings-aware, this will allow you to pass this service not only to controllers.

M
Maxim Fedorov, 2021-08-11
@Maksclub

will use __construct, then I will need to duplicate all constructor arguments

Each controller has its own dependencies and it's good
if something is duplicated - it's an accident, well, more precisely, it's not a double in the classical sense, the logic is not duplicated here

A
AlpineMilk, 2021-08-11
@AlpineMilk

You can use the annotation@required

/**
     * @required
     */
    public function setSetting(Setting $setting)
    {
        $this->setting = $setting;
    }

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question