Страницы

Поиск по вопросам

суббота, 28 декабря 2019 г.

Laravel: сервис-контейнеры и сервис-провайдеры. Что это и зачем?

#php #laravel


Недавно перешел с фреймворка CodeIgniter (в котором была простая схема MVC) на изучение
Laravel. Фреймворк очень понравился, но есть некоторые моменты, которые я никак не
могу понять, сколько бы не читал документацию. В частности что такое сервис-контейнеры
и сервис-провайдеры и зачем они нужны. Прошу помочь разобраться.  
    


Ответы

Ответ 1



Ну что-ж, попробую объяснить. С Богом! Все возможности я не опишу, просто попытаюсь дать представление и возможно (скорее всего) не совсем точно. Введение: Вообще эти штуки нужны, в первую очередь, для удобства, как плюс — они реализуют паттерн Dependency Injection. С него и начнём. class myClassA { private $obj; function __construct() { $this->obj = new myClassB; } } Красиво?... Нет! Почему? А в друг нам понадобится, чтобы при создании класс myClassB принимал аргументы в свой конструктор? Придётся переписывать. Вы скажете: "перепишу, ничего страшного", но если таких классов myClassA много, то тут уже косяк — надо разруливать, для этого и придумали паттерн, глянем на то, как надо: function __construct(myClassB $classB) { $this->obj = $classB; } Т.е. мы в конструктор передали уже существующий класс myClassB, но где мы его создали? ведь new нигде не писали. Магия? Можно представить, что мы в Хогвартсе и зачитать заклинание "PHP Reflection" (но это уже отдельная тема). Так а Laravel что? Сложно описать, давайте покажем, переведем наш класс myClassA в контроллер: namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Requests; use App\MyClass\MyClassB; class MyController extends Controller { private $obj; public function __construct(MyClassB $classB) { $this->obj = $classB; } public function index() { echo $this->obj->myMethod(); } } Но нужно теперь и MyClassB создать. Сразу вспомним начало, нам нужно учитывать что класс должен что-то принимать. Создаем папочку app\MyClass и там файлик MyClassB.php с текстом: action = $action; } public function myMethod() { return "Laravel - ".$this->action; } } Ну что? Запускаем? Не-не... надо ж как-то передать какой нибудь $action в конструктор. Плюс, вот тут уже справедливо сказать, что Laravel не понимает, что у нас с зависимостями (ведь класс myClassA зависит от myClassB). А мы только тупо создали файл и в нём класс, надо что-то ещё. Вот тут НАКОНЕЦ-ТАКИ мы переходим к Сервис-провайдеру, просим всемогущего Artisan создать нам новый провайдер: php artisan make:provider MyProvider Далее, во вторую вкладочку открываем документацию и заполняем его (а сам файлик должен быть тут - app\Providers): app->bind(MyClassB::class, function(){ return new MyClassB('MyAction'); }); } } Из документации видно, что внутри метода register() как раз и должны быть наши связывания, простенькие примеры: // Если хотим, чтобы каждый вызов, возвращался новый класс $this->app->bind(MyClassB::class, function(){ return new MyClassB('MyAction'); }); // Если хотим, чтобы был создан только один объект и всегда он возвращался $this->app->singleton(MyClassB::class, function(){ return new MyClassB('MyAction'); }); Стоит также одним глазком глянуть в документацию на строчку: Если вы откроете файл config/app.php, поставляемый с Laravel, то увидите массив providers. В нём перечислены все классы сервис-провайдеров, которые загружаются для вашего приложения. Наш MyProvider тоже должен быть в этом списке. Теперь можно запускать и, в принципе, всё должно работать. Теперь мы знаем, что MyProvider - это и есть Сервис-провайдер (внимательные заметили, что расширяет наш класс именно ServiceProvider. Совпадение?). Грубо говоря, это инструкция, которая создаст нам класс, чтобы другие классы могли им пользоваться (и в других местах). Вроде разобрались с Сервис-провайдерами. Абасаца... А теперь еще Сервис-контейнер. Продолжаем стори... Так а где был создан MyClassB? Так вот, можно сказать, что он был создан в Сервис-контейнере. Srsly? Можно сказать что Сервис-контейнер — это массив (array(key => value)), где ключ MyClassB::class, а значение это new MyClassB('MyAction'), и запись мы произвели с помощью метода bind. Но, на самом деле, помимо условного массива — это целый механизм, который позволяет делать такую магию в целом, вспоминаем наше заклинание из Хогвартса и зовется сие - Сервисом. Дочитали до конца? Бонусы: 1) Когда мы в app.php прописали провайдер, он будет автоматически создан и занесён в контейнер, а если он не так часто используется, зачем его каждый раз создавать? Для этого воспользуемся термином Отложенный-провайдер, для этого в нашем провайдере (мы-же теперь знаем, что это), нужно прописать protected $defer = true; тогда он будет создан только по запросу. 2) Мы можем и в коде обращаться к нашему контейнеру, например $getClass = $this->app->make('MyClassB'); (либо resolve('MyClassB');) (подробнее в документации) P.S. В конце хочу добавить, что критику принимаю :) Подправим, допилим, может в будущем кому пригодится.

Ответ 2



В кратце Сервис-контейнер - инструмент для управления зависимостями классов и внедрения зависимостей. Сервис-провайдеры - центральное место для конфигурирования. Вот пару постов: https://webzone.kz/publication/laravel-service-container https://webzone.kz/publication/laravel-service-providers

Комментариев нет:

Отправить комментарий