Внедрение зависимостей в Slim Framework — передача контейнера в ваши собственные классы

Я уже комментировал эту ветку, но она кажется мертвой, поэтому я открываю новую: Инъекция зависимостей Slim Framework 3

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

Тем не менее, OP спросил, возможно ли заставить Slim внедрять зависимости ALL в свои классы.

Мне также интересно узнать, есть ли способ сделать это, поскольку кажется, что это что угодно, кроме DRY, если вам нужно передать контейнер каждому классу, который вы хотите использовать.

Например, если я хочу использовать одну из функций Slim (например, выполнить перенаправление в одном из моих собственных классов), я не могу использовать это в соответствии с документацией:

$res->withStatus(302)->withHeader('Location', 'your-new-uri');

Потому что $res (объект ответа) не входит в рамки моего класса, если только я не введу/не передам его.

Проблема в том, что если у меня есть, скажем, 100 классов, нужно ли мне передавать (или вводить) контейнер 100 раз? Это кажется очень, очень утомительным.

В таких фреймворках, как CakePHP, вы можете использовать «AppController», чтобы глобально делать подобные вещи, то есть определять вещи один раз и делать их доступными во ВСЕХ ваших классах. Слим не предоставляет такой функционал? Если нет, то это серьезный недостаток, имхо.


Правка. Я добавляю это из одного из своих комментариев, чтобы попытаться объяснить проблему подробнее:

Если вы посмотрите на учебник по первому приложению — http://slimframework.com/docs/tutorial/first-app.html — они добавляют соединение с базой данных PDO в контейнер.

Допустим, у меня есть 100 отдельных классов в подкаталоге (в примере есть каталог ../classes/) и я автоматически загружаю их в index.php, используя spl_autoload_register(). Контейнер НЕ доступен ни в одном из этих классов.

Если бы мне приходилось передавать что-то 100 раз, каждый раз, когда я использую один из своих классов, только для того, чтобы получить соединение PDO (и это только один пример), тогда это делает код очень повторяющимся, то есть не СУХИМ.


person Andy    schedule 21.10.2016    source источник
comment
Поскольку Slim по умолчанию использует Pimple в качестве DIC, возможно, вам следует изучить это? pimple.sensiolabs.org   -  person Magnus Eriksson    schedule 21.10.2016
comment
Насколько мне известно, у Pimple нет способа автоматически разрешать зависимости конструктора. Поэтому вам нужно зарегистрировать все классы в контейнере. Однако я не понимаю, почему это пойдет против DRY? Когда вы регистрируете свои классы в контейнере, вы по-прежнему вводите зависимости из контейнера, поэтому все классы регистрируются и разрешаются только один раз... Примеры см. на сайте Pimples.   -  person Magnus Eriksson    schedule 21.10.2016
comment
@MagnusEriksson, чтобы привести пример - если вы посмотрите на руководство по первому приложению - slimframework. com/docs/tutorial/first-app.html — они добавляют соединение с базой данных PDO в контейнер. Теперь... допустим, у меня есть 100 отдельных классов в подкаталоге (в примере есть каталог ../classes/) и я автоматически загружаю их в index.php с помощью spl_autoload_register(). Контейнер НЕ доступен ни в одном из этих классов. Если мне нужно написать что-то 100 раз, чтобы получить соединение с PDO в каждом классе, это не СУХОЙ, потому что я повторяю один и тот же код 100 раз.   -  person Andy    schedule 21.10.2016
comment
Вы все равно создадите только одно соединение PDO и передадите это соединение всем классам. Конечно, вам нужно внедрить один и тот же экземпляр во все классы, но это не противоречит DRY (вы не повторяете один и тот же код, вы просто повторно используете один и тот же экземпляр класса). Вы по-прежнему создаете каждый класс только один раз (включая PDO) и внедряете их зависимости. Однако именно поэтому я не очень люблю Pimple и ему подобные контейнеры. Я предпочитаю контейнеры, такие как Illuminate\Container, которые используют отражения для автоматического разрешения внедрения зависимостей.   -  person Magnus Eriksson    schedule 21.10.2016
comment
Можно также возразить, что если вы создаете приложение со 100-1000 классами, вам может понадобиться более крупный фреймворк, такой как Laravel, который делает все, что вы хотите, из коробки.   -  person Magnus Eriksson    schedule 21.10.2016
comment
@MagnusEriksson спасибо за информацию. Это тот бит, в котором вы указали передать это соединение всем классам — как это делается? Похоже, вы должны делать это каждый раз, когда где-то в приложении создается экземпляр класса? Я использовал только 100 классов в качестве примера, чтобы проиллюстрировать тот факт, что если у вас есть много классов, это становится немного повторяющимся. Приложение, над которым я на самом деле работаю, имеет только около 10, и причина, по которой я хотел использовать Slim, заключается в том, что я думал, что он будет проще и легче, чем CakePHP, особенно v3, который очень тяжелый IMO.   -  person Andy    schedule 21.10.2016
comment
Может быть, это: разрешение контейнера. Если вы строите свои маршруты с вызываемыми объектами (у них есть функция __invoke), Slim will call it’s constructor with the container as the first argument.   -  person Zimmi    schedule 21.10.2016
comment
Да, вам нужно передать его всем классам, но вы не должны создавать экземпляры классов в разных местах вашего приложения, вы должны зарегистрировать их все в контейнере и просто получить тот, который вам нужен, когда он вам нужен. Они будут загружаться лениво и создаваться только при первом использовании. Создайте один файл, в котором вы регистрируете все классы, прежде чем запускать приложение.   -  person Magnus Eriksson    schedule 21.10.2016
comment
@Zimmi - Таким образом, вы просто будете использовать контейнер в качестве локатора сервисов, и вы создадите одну и ту же жесткую зависимость во всех своих классах. Также будет сложно писать тесты, если вы хотите имитировать один класс только в некоторых классах, которые его используют.   -  person Magnus Eriksson    schedule 21.10.2016
comment
@MagnusEriksson извиняется, но я не понимаю, что вы имеете в виду под созданием одного файла, в котором вы регистрируете все классы. Я использую spl_autoload_register() для автоматической загрузки всех классов в моем каталоге «classes/», но помимо этого я не уверен, что делать. Есть ли какие-нибудь закодированные примеры, на которые я могу посмотреть для этого? Большое спасибо за вашу помощь.   -  person Andy    schedule 21.10.2016
comment
Вы проверили первую ссылку, которую я разместил? Контейнер Slims основан на Pimple, поэтому вы можете просмотреть эту страницу, чтобы получить более подробное представление о том, как использовать контейнер. Взгляните, например, на часть об Определении служб. Определение сервисов для контейнера и классов автозагрузки - это две разные вещи...   -  person Magnus Eriksson    schedule 21.10.2016


Ответы (2)


Slim поставляется с Pimple по умолчанию. Некоторые разработчики утверждают (и я склонен с ними согласиться), что Pimple — это не контейнер внедрения зависимостей, а локатор сервисов, поскольку он не разрешает зависимости сам по себе, их нужно регистрировать.

Slim 3 работает с любым менеджером зависимостей, который реализует интерфейс взаимодействия контейнеров, что делает PHP-DI. .

Выберите этот пакет. Это то, что я использую в своих проектах, и это просто потрясающе благодаря автоподключению. . Проще говоря, PHP-DI читает конструктор класса и понимает, что нужно внедрить, поэтому вам не нужно регистрировать зависимости, как в случае с Pimple.

Иногда я думаю (надеюсь?), что PHP-DI заменит Pimple в качестве контейнера DI Slim по умолчанию, потому что он просто более продвинутый.

Вот как вы поступите с Pimple:

<?php
namespace Controllers;

class UsersController
{
    // Inject Container in controller (which is bad, actually)
    public function __construct(ContainerInterface $container)
    {
        // grab instance from container
        $this->repository = $container['userRepository'];
    }

    // Handler of a route
    public function getAllUsers($request, $response)
    {
        $user = $this->repository->getAllUsers();
        return $response->withJson($users);
    }
}

Вот тот же контроллер с PHP-DI:

<?php
namespace Controllers;

class UsersController
{
    // Declare your dependencies in constructor:
    // PHP-DI will find the classes and inject them automatically
    public function __construct(UserRepository $repository)
    {
        $this->repository = $repository;
    }

    // Handler of a route
    public function getAllUsers($request, $response)
    {
        $user = $this->repository->getAllUsers();
        return $response->withJson($users);
    }
}

Проблема в том, что если у меня есть, скажем, 100 классов, нужно ли мне передавать (или вводить) контейнер 100 раз? Это кажется очень, очень утомительным.

Если вы используете Slim в комплекте с PHP-DI, проблема решается автоматически с помощью автоматического связывания. :)

person Georgy Ivanov    schedule 21.10.2016
comment
есть ли у вас какие-либо комментарии к приведенному ниже решению (пользователь «Одри Робертс»)? Я принял ваш ответ, но другое решение не зависит от использования вещей, которые не поставляются с установкой Slim по умолчанию, и кажется проще. - person Andy; 24.10.2016
comment
Решение Одри полностью приемлемо; это предусмотрено в учебнике Слима. Однако передача контейнера в качестве аргумента конструктора на самом деле сводит на нет саму цель внедрения зависимостей и усложняет тестируемость. Это только кажется проще. - person Georgy Ivanov; 24.10.2016
comment
Это очень беспокоит (и несколько раздражает), что документация для Slim рассказывает о внедрении зависимостей, и выясняется, что на самом деле он не использует его должным образом. На самом деле это было не то, о чем я был должным образом осведомлен и провел много времени, читая о DI и Slim, только чтобы узнать, что он даже не использует его должным образом - очень расстраивает. Тем не менее, я рассмотрю автопроводку и Slim Bridge более подробно, спасибо за предложения. - person Andy; 24.10.2016

Самый простой способ сделать это так:

index.php

$app->get('/mytest', '\TestController:mytest');

TestController.php

class TestController {

    protected $ci;

    public function __construct(Slim\Container $ci) {
        //var_dump($ci);
        $this->ci = $ci;
    }

    public function mytest() {
        $sql = ''; // e.g. SQL query
        $stmt = $this->ci->db->prepare($sql);
    }
}

Я не уверен, что это «правильный» способ сделать это, но происходит то, что конструктор TestController получает контейнер в качестве первого аргумента. Это упоминается в их документации: http://www.slimframework.com/docs/objects/router.html#container-resolution

Поэтому, когда вы используете такую ​​функцию, как TestController::mytest(), она имеет доступ ко всему в контейнере, например к экземпляру базы данных PDO, который вы настроили в index.php (если следуете их учебному пособию по Первому приложению).

Как я уже сказал, я не уверен, что это «правильный» способ сделать это, но он работает.

Если вы раскомментируете строку var_dump($ci), вы увидите объект Slim Container.

Если у кого-то есть какие-либо отзывы по этому поводу, пожалуйста, напишите, мне было бы интересно узнать.

person John    schedule 22.10.2016