Недавно перешел с фреймворка CodeIgniter (в котором была простая схема MVC) на изучение Laravel. Фреймворк очень понравился, но есть некоторые моменты, которые я никак не могу понять, сколько бы не читал документацию. В частности что такое сервис-контейнеры и сервис-провайдеры и зачем они нужны. Прошу помочь разобраться.
Ну что-ж, попробую объяснить. С Богом!
Все возможности я не опишу, просто попытаюсь дать представление и возможно (скорее всего) не совсем точно.
Введение:
Вообще эти штуки нужны, в первую очередь, для удобства, как плюс — они реализуют паттерн 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
с текстом:
<?php
namespace App\MyСlass;
class MyClassB
{
private $action;
public function __construct($action) {
$this->action = $action;
}
public function myMethod() {
return "Laravel - ".$this->action;
}
}
Ну что? Запускаем? Не-не... надо ж как-то передать какой нибудь $action
в конструктор. Плюс, вот тут уже справедливо сказать, что Laravel
не понимает, что у нас с зависимостями (ведь класс myClassA
зависит от myClassB
). А мы только тупо создали файл и в нём класс, надо что-то ещё. Вот тут НАКОНЕЦ-ТАКИ мы переходим к Сервис-провайдеру
, просим всемогущего Artisan
создать нам новый провайдер:
php artisan make:provider MyProvider
Далее, во вторую вкладочку открываем документацию и заполняем его (а сам файлик должен быть тут - app\Providers
):
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\MyClass\MyClassB;
class MyProvider extends ServiceProvider
{
public function boot() {}
public function register()
{
$this->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. В конце хочу добавить, что критику принимаю :) Подправим, допилим, может в будущем кому пригодится.
Оборудование для ресторана: новинки профессиональной кухонной техники
Частный дом престарелых в Киеве: комфорт, забота и профессиональный уход
В документации полно примеров как вызвать метод контроллера для обработки конкретного urlНапример:
помогите пожалуйста примером у кого есть, какие параметры требует функция php ldap_rename(), не совсем понимаю что она требует, глядя на документацию...
Скрипт 1 находится на: domains/site/script/functionphp Скрипт 2 на: domains/site/include/connect
нашел в доке такую конструкцию, правильно ли я понимаю, что так можно проверить по хеш данного кошелька, что был осуществлен перевод? И если...