Where to move reusable controller injections and related methods

I’m starting a new slim 4 application, and to not describe each controller in the container and to not pass a container instance to the controllers I’m using PHP-DI autowiring.

But with such an approach I need to inject all dependencies to each controller and actually it’s correct, but most of those dependencies are reusable by all controllers. Example:

public function __construct(
    Environment $twig,
    Messages $flashMessage,
    RouteCollectorInterface $routeCollector
)
{
    $this->render = $twig;
    $this->flashMessage = $flashMessage;
    $this->routeCollector = $routeCollector;
}

AndI’m looking for a way to make a base controller, inject all reusable dependencies to it so when DIc create/autowire each new controller, all dependencies already injected b-z controller extend base one? Also, the base controller probably should use method injection, otherwise, I still need to create a constructor and pass all injection in each controller to the base controller like this:

public function __construct(
    Environment $twig,
    CommandBus $commandBus,
    Messages $flashMessage,
    RouteCollectorInterface $routeCollector
)
{
     parent::__construct($twig, $flashMessage, $routeCollector);
    $this->commandBus = $commandBus;
}

which is probably the same that inject dependencies to each controller without base controller at all.

I tried to configure base contoller:

   $definitions[BaseController::class] = static function(ContainerInterface $container): BaseController {
        /** @var BaseController $controller */
        $controller =new BaseController();
        $controller->setView($container->get(Environment::class));
        $controller->setRouter($container->get(RouteCollectorInterface::class));
        $controller->setFleshMessage($container->get(Messages::class));

        return $controller;
    };

but it’s not working, and dependencies are null for each controller which extending base controller.

PHP-DI http://php-di.org/doc/best-practices.html#writing-controllers propose to use property injection, but it still looks like a boilerplate for me to inject all reusable injections for each controller and it’s breaking encapsulation when we inject into private properties.

One of the solutions I can think is to make some kind of ControllerHelper with all reusable dependencies as a bridge/wrapper and inject it instead of all those dependencies, it 'll reduce the amount of duplicated code. But still, maybe there is a better solution with the usage of base controller???

I know that it’s actually how it works in OOP, if you extend base class you need to pass all the dependencies to it. But to reduce some duplication, some magic would be good here :slight_smile: also it’s not an external package that will be used by someone else so probably such kind of magic is okay if it’s possible to achieve somehow at all.

in other words, what I’m trying to achieve is:

 class SomeController extends BaseController {
// in this case constructor is empty 

        public function index(RequestInterface $request, ResponseInterface $response) {
             return $this->render($response, $templateName, [somedata]);
        }
    }

where $this-render is a BaseController class method which using template engine dependency to render view:

public function render(ResponseInterface $request, string $templateName, array $data): ResponseInterface {
          $response->getBody()->write(
            $this->render->render(
                $templateName,
                $data
            )
        );

        return $response;
}

as you can see I do not want to inject dependencies to the SomeController but somehow inject them once to the BaseController

My personal advice is: Forget controllers and don’t use inheritance.

Controllers are doing too much and breaking the single responsibility principle. You don’t need inheritance or a bridge/wrapper etc… Keep it simple :wink:

An Action (handler) is a much better way to achieve what you are looking for. It fits perfect into the world of dependency injection.

Read more:

1 Like

I agree that controllers breaking the single responsibility principles but in my opinion, it’s a trade-off between having one class and many classes, and I think if you are holding all the logic in those classes, then I can agree that handler is better, otherwise, controllers become very huge. And also to not thinking about a single responsibility principle when I’m using controllers, I’m thinking about controllers like a repository for Http calls :slight_smile:

And I also agree that you need to avoid using inheritance and to use composition instead . But again it’s a trade-off :slight_smile: and in some cases, like here I think it would be good to reduce boiler platting, to not inject same data again to each class constructor, and actually no matter what I’ll use controller or handler, I’ll still need to inject template engine, route etc., to most of them.

Also in my case, this specific app is more like a frontend app for the admin dashboard. Under the hood, I’m using DDD with CQRS, so what these controllers really doing is validating request data and calling internal API to send the data to the appropriate microservice, or few of them if needed, and that service(s) already taking care of the data using command bus and etc. So in my case, it’s orchestrating the API, it’s the same when front-end application calling the API endpoints from the JS. But in case of admin dashboard, it’s saving the time to use the server-side engine and calling the API from the backend. So I think it’s not a big deal to break some rules here, to make code base smaller here, and making a development prcess simpler (avoiding to copy/paste same dependencies again and again )