• Время чтения ~5 мин
  • 03.06.2022

Для тех, кто не использовал активно поставщиков услуг в Laravel, это мистический «термин»: какую «услугу» они на самом деле «предоставляют» и как именно все это работает? Я объясню это в этой статье.


Поставщики услуг Laravel по умолчанию

Начнем с поставщиков услуг по умолчанию, включенных в Laravel, все они находятся в папке app/Providers:

  • AppServiceProvider
  • AuthServiceProvider
  • BroadcastServiceProvider
  • EventServiceProvider
  • RouteServiceProvider

Все они представляют собой классы PHP, каждый из которых связан со своей темой: общее «приложение», аутентификация, трансляция, события и маршруты. Но все они имеют одну общую черту: метод boot().

Внутри этого метода вы можете написать любой код, связанный с одним из этих разделов: аутентификацией, событиями, маршрутами и т. д. Другими словами, поставщики услуг — это просто классы для регистрации некоторых глобальных функций.

Они разделены как «поставщики», потому что они выполняются очень рано в жизненном цикле приложения, поэтому здесь удобно что-то глобальное, прежде чем сценарий выполнения дойдет до моделей или контроллеров.

Наибольшая функциональность содержится в RouteServiceProvider, давайте взглянем на его код:

class RouteServiceProvider extends ServiceProvider
{
    public const HOME = '/dashboard';
 
    public function boot()
    {
        $this->configureRateLimiting();
 
        $this->routes(function () {
            Route::prefix('api')
                ->middleware('api')
                ->group(base_path('routes/api.php'));
 
            Route::middleware('web')
                ->group(base_path('routes/web.php'));
        });
    }
 
    protected function configureRateLimiting()
    {
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
        });
    }
}

Это класс, в котором настраиваются файлы маршрутов с включенными по умолчанию routes/web.php и routes/api.php.Обратите внимание, что для API также существуют разные конфигурации: префикс конечной точки /api и промежуточное ПО api для всех маршрутов.

Вы можете редактировать этих поставщиков услуг по своему усмотрению, они не находятся в папке /vendor. Типичная настройка этого файла происходит, когда у вас много маршрутов и вы хотите разделить их в своем пользовательском файле.Вы создаете routes/auth.php и помещаете туда маршруты, а затем «включаете» этот файл в методе boot() в RouteServiceProvider, просто добавьте третье предложение:

`Route::middleware('web') // or maybe you want another middleware?
    ->group(base_path('routes/auth.php'));

Другие поставщики услуг по умолчанию имеют другие функции, вы можете проанализировать их самостоятельно.За исключением AppServiceProvider, он пуст, как заполнитель для добавления любого кода, связанного с некоторыми глобальными настройками приложения.

Одним из популярных примеров добавления кода в AppServiceProvider является отключение ленивая загрузка в Eloquent.Для этого вам просто нужно добавить две строки в Метод загрузки():

// app/Providers/AppServiceProvider.php
use Illuminate\Database\Eloquent\Model;
 
public function boot()
{
    Model::preventLazyLoading(! $this->app->isProduction());
}

Это вызовет исключение, если какая-либо модель отношений не загружена, что вызывает так называемую проблему запроса N+1 с производительностью.


Когда казнят поставщиков услуг?

Если вы посмотрите на официальную документацию о жизненном цикле запросов, то увидите, что в самом начале выполняются следующие действия:

  • public/index.php
  • bootstrap/app.php
  • app/Http/Kernel.php and its Middlewares
  • Service Providers: exactly our topic of this article

Какие поставщики загружаются? Он определен в файле config/app.массив php:

return [
 
	// ... other configuration values
 
    'providers' => [
 
        /*
         * Laravel Framework Service Providers...
         */
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,
 
        // ... other framework providers from /vendor
        Illuminate\Validation\ValidationServiceProvider::class,
        Illuminate\View\ViewServiceProvider::class,
 
        /*
         * PUBLIC Service Providers - the ones we mentioned above
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,
 
    ],
 
];

Как видите, в папке /vendor есть список поставщиков услуг, не являющихся общедоступными, их не нужно трогать/редактировать. Те, которые нас интересуют, находятся внизу, а BroadcastServicerProvider отключен по умолчанию, вероятно, потому, что он редко используется.

Все эти поставщики услуг выполняются сверху вниз, повторяя этот список дважды.

Первая итерация ищет необязательный метод register(), который можно использовать для инициации чего-либо перед методом boot(). Я никогда не использовал его в своем опыте.

Затем во второй итерации выполняется метод boot() всех поставщиков. Опять же, один за другим, сверху вниз по массиву 'providers'.

И затем, после обработки всех поставщиков услуг, Laravel переходит к разбору маршрута, выполнению контроллера, использованию моделей и т. д.


Создайте своего поставщика услуг

В дополнение к существующим файлам по умолчанию вы можете легко создать своего поставщика услуг, связанного с некоторыми другими темами, отличными от стандартных, таких как auth/event/routes.

Вполне типичным примером является конфигурация, связанная с представлениями Blade.Если вы хотите создать свою директиву Blade, вы можете добавить этот код в метод boot() любого поставщика услуг, включая метод AppServiceProvider по умолчанию, но довольно часто разработчики создают отдельный ViewServiceProvider .

Вы можете сгенерировать его с помощью этой команды:

php artisan make:provider ViewServiceProvider

Будет создан шаблон по умолчанию:

namespace App\Providers;
 
use Illuminate\Support\ServiceProvider;
 
class ViewServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
 
    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}

Вы можете удалить метод register() и внутри boot() добавить код директивы Blade:

use Illuminate\Support\Facades\Blade;
 
public function boot()
{
    Blade::directive('datetime', function ($expression) {
        return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
    });
}

Еще один пример ViewServiceProvider касается View Composers, вот фрагмент из официальной документации Laravel:

use App\View\Composers\ProfileComposer;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
 
class ViewServiceProvider extends ServiceProvider
{
    public function boot()
    {
        // Using class based composers...
        View::composer('profile', ProfileComposer::class);
 
        // Using closure based composers...
        View::composer('dashboard', function ($view) {
            //
        });
    }
}

Для выполнения этот новый провайдер должен быть добавлен в массив провайдеров в config/app.php, как указано выше:

return [
    // ... other configuration values
 
    'providers' => [
 
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,
 
        // Add your provider here
        App\Providers\ViewServiceProvider::class,
    ],
];

Примеры из проектов с открытым исходным кодом

Наконец, я хочу упомянуть несколько примеров из свободно доступных проектов Laravel.

1. spatie/freek.

namespace App\Providers;
 
use App\Http\Components\AdComponent;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
 
class BladeComponentServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Blade::component('ad', AdComponent::class);
 
        Blade::component('front.components.inputField', 'input-field');
        Blade::component('front.components.submitButton', 'submit-button');
        Blade::component('front.components.textarea', 'textarea');
        Blade::component('front.components.textarea', 'textarea');
        Blade::component('front.components.shareButton', 'share-button');
        Blade::component('front.components.lazy', 'lazy');
        Blade::component('front.components.postHeader', 'post-header');
 
        Blade::component('front.layouts.app', 'app-layout');
    }
}
namespace App\Providers;
 
use App\Helpers\CollectionHelper;
use Illuminate\Support\Collection;
use Illuminate\Support\ServiceProvider;
 
class MacroServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        if (! Collection::hasMacro('sortByCollator')) {
            Collection::macro('sortByCollator', function ($callback, $options = \Collator::SORT_STRING, $descending = false) {
                /** @var Collection */
                $collect = $this;
 
                return CollectionHelper::sortByCollator($collect, $callback, $options, $descending);
            });
        }
 
        if (! Collection::hasMacro('groupByItemsProperty')) {
            Collection::macro('groupByItemsProperty', function ($property) {
                /** @var Collection */
                $collect = $this;
 
                return CollectionHelper::groupByItemsProperty($collect, $property);
            });
        }
 
        if (! Collection::hasMacro('mapUuid')) {
            Collection::macro('mapUuid', function () {
                /** @var Collection */
                $collect = $this;
 
                return $collect->map(function ($item) {
                    return $item->uuid;
                })->toArray();
            });
        }
    }
}
 
namespace App\Providers;
 
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Blade;
use App\Helpers\FileUpload\UploadComponents;
 
class DashboardComponentsServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
 
    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        Blade::directive('uploadForm', function () {
            $component = UploadComponents::getUploadForm();
            $html = '<?php echo \'' . $component . '\'; ?>';
 
            return ('<?php echo "' . $component . '"; ?>');
        });
    }
}

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

Об авторе CrazyBoy49z
WORK EXPERIENCE
Контакты
Ukraine, Lutsk
+380979856297