как правильно внедрить повторно используемую инъекцию контроллера с помощью php-di

Я запускаю новое приложение slim 4, а текущие версии slim — это больше просто маршрутизатор, чем фреймворк, и у меня нет di-контейнера из коробки, поэтому я выбираю PHP-DI в качестве DI-контейнера. А чтобы не описывать каждый контроллер в контейнере и не передавать экземпляр контейнера в контроллеры я использую PHP-DI autowiring.

Но при таком подходе мне нужно внедрить все зависимости в каждый контроллер, и большинство из этих зависимостей можно повторно использовать всеми контроллерами. Пример:

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

Есть ли способ создать базовый контроллер, внедрить в него все повторно используемые зависимости, чтобы при DIc создании/автоподключении каждого нового контроллера все уже внедренные зависимости b-z контроллера расширяли базовый? Кроме того, базовый контроллер, вероятно, должен использовать внедрение методов, иначе мне все равно нужно создать конструктор и передать все инъекции в каждом контроллере базовому контроллеру следующим образом:

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

что, вероятно, то же самое, что внедрять зависимости в каждый контроллер вообще без базового контроллера.

Я попытался настроить базовый контроллер:

   $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;
    };

но это не работает, и зависимости null для каждого контроллера, расширяющего базовый контроллер.

PHP-DI http://php-di.org/doc/best-practices.html#writing-controllers предлагают использовать внедрение свойств, но для меня это все еще выглядит как шаблон для внедрения всех повторно используемых внедрений для каждого контроллера, и это нарушает инкапсуляцию, когда мы внедряем в частные свойства.

Одно из решений, которое я могу придумать, состоит в том, чтобы сделать что-то вроде ControllerHelper со всеми повторно используемыми зависимостями в качестве моста/оболочки и внедрить его вместо всех этих зависимостей, это уменьшит количество дублированного кода. Но все же, может есть лучшее решение???

Обновлено: я знаю, что на самом деле это работает в ООП, если вы расширяете базовый класс, вам нужно передать ему все зависимости. Но чтобы уменьшить некоторое дублирование, здесь было бы неплохо немного магии :) также это не внешний пакет, который будет использоваться кем-то другим, так что, вероятно, такая магия допустима, если ее вообще можно как-то достичь.

Обновлено: другими словами, я пытаюсь добиться следующего:

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

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

где $this-render — это метод класса BaseController, который использует зависимость механизма шаблонов для рендеринга представления:

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

        return $response;
}

как видите, я не хочу внедрять зависимости в SomeController, но каким-то образом внедряю их один раз в BaseController


person Bogdan Dubyk    schedule 17.12.2019    source источник


Ответы (1)


Вы можете попробовать сделать это, как следующий класс контроллера BaseController:

<?php

namespace App\Application;

use Twig\Environment;
use Laminas\Diactoros\Response;
use Psr\Http\Message\ResponseInterface;

abstract class BaseController
{
    private $template;

    public function __construct(Environment $template)
    {
        $this->template = $template;
    }

    public function view(string $template, array $data = []) : ResponseInterface
    {
        $result = $this->template->render($template, $data);

        $response = new Response();
        $response->getBody()->write($result);
        return $response;
    }
}

Затем при вызове вашего контроллера вы можете сделать это:

<?php

namespace App\Controller\Auth;

use App\Application\Controller;

class AuthController extends BaseController
{

    public function showLogin()
    {
        return $this->view('auth/login.html');
    }
}
person Franck    schedule 30.05.2021