+1

[Laravel Architecture] Service Providers là gì?

Như những bài của seri trước thì chúng ta đã hiểu Service Container trong Laravel là gì rồi. Tiếp tục hôm nay chúng ta sẽ đi đến tìm hiểu Service Providers, khi đã hiểu Service Container rồi thì sang phần Service Providers dễ hiểu hơn rất nhiều. Nào chúng ta cùng bắt đầu nhé! Docs của Laravel có nói

Service providers are the central place of all Laravel application bootstrapping. Your own application, as well as all of Laravel's core services, are bootstrapped via service providers.

Tạm dịch

Service Providers là một nơi trung tâm đặt tất cả quá trình khởi động của Laravel. Những ứng dụng của bạn cũng như những service core của Laravel sẽ được khởi động từ Service Providers.

Nghe có vẻ hơi khó hiểu nhỉ. Để mình trình bày lại một cách dễ hiểu hơn nhé. Service Provider là 1 nơi, nơi này để binding tất cả service core của Laravel cũng như service của riêng bạn vào Service Container. Chỉ đơn giản thế thôi. Như vậy Service Container là tool quản lý các class, còn Service Provider là nơi đăng ký các class vào Service Container. Thay vì viết binding các class vào Service container lung tung ở các nơi, thì ta sẽ binding các class này vào Service container ở 1 nơi nhất quán, nơi này là Service Providers. Như vậy nhìn nó chuẩn Laravel và dễ dàng quản lý hơn

Nơi khai báo config/app.php

Tất cả các Service Provider được khai báo trong config/app.php

/*
    |--------------------------------------------------------------------------
    | Autoloaded Service Providers
    |--------------------------------------------------------------------------
    |
    | The service providers listed here will be automatically loaded on the
    | request to your application. Feel free to add your own services to
    | this array to grant expanded functionality to your applications.
    |
    */

    'providers' => [

        /*
         * Laravel Framework Service Providers...
         */
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,
        Illuminate\Bus\BusServiceProvider::class,
        Illuminate\Cache\CacheServiceProvider::class,
        Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
        Illuminate\Cookie\CookieServiceProvider::class,
        Illuminate\Database\DatabaseServiceProvider::class,
        Illuminate\Encryption\EncryptionServiceProvider::class,
        Illuminate\Filesystem\FilesystemServiceProvider::class,
        Illuminate\Foundation\Providers\FoundationServiceProvider::class,
        Illuminate\Hashing\HashServiceProvider::class,
        Illuminate\Mail\MailServiceProvider::class,
        Illuminate\Notifications\NotificationServiceProvider::class,
        Illuminate\Pagination\PaginationServiceProvider::class,
        Illuminate\Pipeline\PipelineServiceProvider::class,
        Illuminate\Queue\QueueServiceProvider::class,
        Illuminate\Redis\RedisServiceProvider::class,
        Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
        Illuminate\Session\SessionServiceProvider::class,
        Illuminate\Translation\TranslationServiceProvider::class,
        Illuminate\Validation\ValidationServiceProvider::class,
        Illuminate\View\ViewServiceProvider::class,

        /*
         * Package Service Providers...
         */

        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,
        App\Providers\RepositoryServiceProvider::class,
        App\Providers\ViewServiceProvider::class,

        /*
         * Custom Provider
         */
        App\Providers\QueryBuilderTimeStampProvider::class,
        Aws\Laravel\AwsServiceProvider::class,

    ],

Mỗi Service Provider làm nhiệm vụ đăng ký 1 hay nhiều Class vào trong Service Container. Ví dụ ta cùng xem AuthServiceProvider xem nó đăng ký những Class nào nhé

<?php

namespace Illuminate\Auth;

use Illuminate\Auth\Access\Gate;
use Illuminate\Auth\Middleware\RequirePassword;
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Support\ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->registerAuthenticator();
        $this->registerUserResolver();
        $this->registerAccessGate();
        $this->registerRequirePassword();
        $this->registerRequestRebindHandler();
        $this->registerEventRebindHandler();
    }

    /**
     * Register the authenticator services.
     *
     * @return void
     */
    protected function registerAuthenticator()
    {
        $this->app->singleton('auth', function ($app) {
            // Once the authentication service has actually been requested by the developer
            // we will set a variable in the application indicating such. This helps us
            // know that we need to set any queued cookies in the after event later.
            $app['auth.loaded'] = true;

            return new AuthManager($app);
        });

        $this->app->singleton('auth.driver', function ($app) {
            return $app['auth']->guard();
        });
    }

    /**
     * Register a resolver for the authenticated user.
     *
     * @return void
     */
    protected function registerUserResolver()
    {
        $this->app->bind(
            AuthenticatableContract::class, function ($app) {
                return call_user_func($app['auth']->userResolver());
            }
        );
    }

    /**
     * Register the access gate service.
     *
     * @return void
     */
    protected function registerAccessGate()
    {
        $this->app->singleton(GateContract::class, function ($app) {
            return new Gate($app, function () use ($app) {
                return call_user_func($app['auth']->userResolver());
            });
        });
    }

    /**
     * Register a resolver for the authenticated user.
     *
     * @return void
     */
    protected function registerRequirePassword()
    {
        $this->app->bind(
            RequirePassword::class, function ($app) {
                return new RequirePassword(
                    $app[ResponseFactory::class],
                    $app[UrlGenerator::class],
                    $app['config']->get('auth.password_timeout')
                );
            }
        );
    }

    /**
     * Handle the re-binding of the request binding.
     *
     * @return void
     */
    protected function registerRequestRebindHandler()
    {
        $this->app->rebinding('request', function ($app, $request) {
            $request->setUserResolver(function ($guard = null) use ($app) {
                return call_user_func($app['auth']->userResolver(), $guard);
            });
        });
    }

    /**
     * Handle the re-binding of the event dispatcher binding.
     *
     * @return void
     */
    protected function registerEventRebindHandler()
    {
        $this->app->rebinding('events', function ($app, $dispatcher) {
            if (! $app->resolved('auth')) {
                return;
            }

            if ($app['auth']->hasResolvedGuards() === false) {
                return;
            }

            if (method_exists($guard = $app['auth']->guard(), 'setDispatcher')) {
                $guard->setDispatcher($dispatcher);
            }
        });
    }
}

Như code ta thấy AuthServiceProvider đăng ký các Class vào Service Container : AuthManager, AuthenticatableContract, RequirePassword hoặc đăng ký lại ( rebinding) các class Request, Events

Tác dụng của Service Provider

1. Là nơi khai báo tất cả các class cần thiết khi muốn khởi động ứng dụng Laravel

Như nhìn thấy ở file config/app.php ở trên thì mặc định trong list providers có 2 phần

1.1. Phần các Service Providers của Laravel Framework

 /*
         * Laravel Framework Service Providers...
         */
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,
        Illuminate\Bus\BusServiceProvider::class,
        Illuminate\Cache\CacheServiceProvider::class,
        Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
        Illuminate\Cookie\CookieServiceProvider::class,
        Illuminate\Database\DatabaseServiceProvider::class,
        Illuminate\Encryption\EncryptionServiceProvider::class,
        Illuminate\Filesystem\FilesystemServiceProvider::class,
        Illuminate\Foundation\Providers\FoundationServiceProvider::class,
        Illuminate\Hashing\HashServiceProvider::class,
        Illuminate\Mail\MailServiceProvider::class,
        Illuminate\Notifications\NotificationServiceProvider::class,
        Illuminate\Pagination\PaginationServiceProvider::class,
        Illuminate\Pipeline\PipelineServiceProvider::class,
        Illuminate\Queue\QueueServiceProvider::class,
        Illuminate\Redis\RedisServiceProvider::class,
        Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
        Illuminate\Session\SessionServiceProvider::class,
        Illuminate\Translation\TranslationServiceProvider::class,
        Illuminate\Validation\ValidationServiceProvider::class,
        Illuminate\View\ViewServiceProvider::class,

1.2. Phần là các Application Service Provider

/*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,
        App\Providers\RepositoryServiceProvider::class,
        App\Providers\ViewServiceProvider::class,

2. Là nơi để khai báo các Service của bạn để bạn đăng ký vào Service Container, và sử dụng mọi nơi trong khi coding

Khi bạn muốn dùng 1 class bạn tự viết trong Laravel thì chúng ta sẽ ném class này vào Service Container cho nó quản lý, và khi dùng thì lôi ra dùng thôi. Khi ném vào Service Container thì chúng ta sẽ sử dụng Service Provider Ví dụ chúng ta có 1 class app/Providers/RepositoryServiceProvider.php B1. Viết class này trong folder app/Providers

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class RepositoryServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(
            \App\Repositories\AdminUserRepositoryInterface::class,
            \App\Repositories\AdminUserRepository::class
        );
        $this->app->bind(
            \App\Repositories\AdminUserForgotPasswordRepositoryInterface::class,
            \App\Repositories\AdminUserForgotPasswordRepository::class
        );
        $this->app->bind(
            \App\Repositories\EventSettingRepositoryInterface::class,
            \App\Repositories\EventSettingRepository::class
        );
        $this->app->bind(
            \App\Repositories\AdminUserEventSettingRepositoryInterface::class,
            \App\Repositories\AdminUserEventSettingRepository::class
        );
        $this->app->bind(
            \App\Repositories\ContentRepositoryInterface::class,
            \App\Repositories\ContentRepository::class
        );
        $this->app->bind(
            \App\Repositories\ContentCategoryRepositoryInterface::class,
            \App\Repositories\ContentCategoryRepository::class
        );
        $this->app->bind(
            \App\Repositories\UserRepositoryInterface::class,
            \App\Repositories\UserRepository::class
        );
        $this->app->bind(
            \App\Repositories\ParticipantInfoRepositoryInterface::class,
            \App\Repositories\ParticipantInfoRepository::class
        );
        $this->app->bind(
            \App\Repositories\UserStatusRepositoryInterface::class,
            \App\Repositories\UserStatusRepository::class
        );
        $this->app->bind(
            \App\Repositories\AdminCompanyRepositoryInterface::class,
            \App\Repositories\AdminCompanyRepository::class
        );
        $this->app->bind(
            \App\Repositories\ViewLogRepositoryInterface::class,
            \App\Repositories\ViewLogRepository::class
        );
        $this->app->bind(
            \App\Repositories\ContentRepositoryInterface::class,
            \App\Repositories\ContentRepository::class
        );
        $this->app->bind(
            \App\Repositories\OptionColumnRepositoryInterface::class,
            \App\Repositories\OptionColumnRepository::class
        );
        $this->app->bind(
            \App\Repositories\ReserveManageRepositoryInterface::class,
            \App\Repositories\ReserveManageRepository::class
        );
        $this->app->bind(
            \App\Repositories\ContactRepositoryInterface::class,
            \App\Repositories\ContactRepository::class
        );
        $this->app->bind(
            \App\Repositories\ContactCategoriesRepositoryInterface::class,
            \App\Repositories\ContactCategoriesRepository::class
        );
        $this->app->bind(
            \App\Repositories\MailSendSettingRepositoryInterface::class,
            \App\Repositories\MailSendSettingRepository::class
        );
        $this->app->bind(
            \App\Repositories\ExtractSettingRepositoryInterface::class,
            \App\Repositories\ExtractSettingRepository::class
        );
        $this->app->bind(
            \App\Repositories\UserForgotPasswordRepositoryInterface::class,
            \App\Repositories\UserForgotPasswordRepository::class
        );
        $this->app->bind(
            \App\Repositories\AdminUserAdminCompaniesRepositoryInterface::class,
            \App\Repositories\AdminUserAdminCompaniesRepository::class
        );
        $this->app->bind(
            \App\Repositories\LiveSettingRepositoryInterface::class,
            \App\Repositories\LiveSettingRepository::class
        );
        $this->app->bind(
            \App\Repositories\AdminMailSendSettingRepositoryInterface::class,
            \App\Repositories\AdminMailSendSettingRepository::class
        );
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        
    }
}


B2: Đăng ký Service Providers trong config/app

'providers' => [
        /*
         * Custom Provider
         */
        App\Providers\RepositoryServiceProvider::class,

    ],

B3: Lôi 1 class AdminUserRepository trong này ra dùng

app('AdminUserRepository')->getList();

3. Làm việc với các Laravel package

Khi làm việc với 1 package viết cho Laravel, điều hiển nhiên là package này phải cung cấp 1 cách thức để ae coder dùng các hàm trong package này. Theo thiết kế của các package viết cho Laravel thì nó phải cung cấp sẵn các Service Providers của riêng nó để bạn khai báo vào Service Providers của Laravel. Hãy cùng xem xét ví dụ sau: Chúng ta cần dùng các dịch vụ của AWS như s3 chẳng hạn vào dự án. Chúng ta sẽ quyết định sử dụng package của Aws, có 2 package: 1. SDK Aws cho Php của Amazon https://github.com/aws/aws-sdk-php 2. Dùng Laravel package cho Aws https://github.com/aws/aws-sdk-php-laravel Nếu dùng cách 1 thì mỗi lần dùng 1 service như S3 thì ta phải new ClientS3() ... như thế ko giống với Laravel Architecture lắm, ta nên binding ClientS3 này vào Service Container rồi lôi ra dùng. Như vậy ta sẽ dùng luôn cách thứ 2 vì package Aws viết riêng cho Laravel, cung cấp sẵn Service Providers để ta đăng ký các class của Aws vào Service Container

Dùng Laravel package cho Aws thì ta cần làm các bước sau:

1. Đăng ký Service Providers của package này vào Laravel

'providers' => array(
        // ...
        Aws\Laravel\AwsServiceProvider::class,
    )

2. Lôi ra dùng

$s3 = app('aws')->createClient('s3');

All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.