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 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