Laravel 服務容器和提供器

simplewater發表於2018-07-06

看到laravel手冊的服務容器和提供器模組,概念有些模糊,網上搜尋+動手實踐了一番,記錄理解。

laravel框架預設載入了很多的服務,一個服務就是一個類,比如Auth,Cache,Route等等。如果想自定義一個服務怎麼辦,很簡單,自己建立一個類然後再繫結就行。服務容器和服務提供器是沒有直接關係的,一般都是按照什麼提供器就提供什麼服務的規則來命名兩者,但是你建立一個 火車serviceprovider, 然後再這個火車serviceprovider裡繫結汽車類,那麼火車serviceprovider提供的就是汽車物件,雖然用起來很尷尬,但你自己定義的你自己肯定會用的,別人估計是哭著用完的。
自定義的功能類所具有的功能如果需要規範點,那就先寫個介面,比如Cache類,需要擁有get,set方法等,寫個介面加以規範,以後用的時候也只需要呼叫實現了這個介面的類就行,不寫介面也無所謂。

1、建立介面類,介面類的名字隨便起,但是最好還是規範點,自己看著也舒服
建立 app\Contract\SktContract.php 介面類

namespace App\Contract;

interface SktContract
{
    //展示隊伍隊員名單
    public function list_team($team_name);
}

2、接下來按照這個介面來實現你的功能類
建立 app\Service\SktService.php

namespace App\Service;
use App\Contract\SktContract;
class SktService implements SktContract
{
    public $name = null;
    //展示隊伍隊員名單
    public function list_team($team_name)
    {
        echo $team_name."的隊員名單是Faker-bengi";
    }
}

3、既然功能類寫好了,那就要把他裝在服務提供器中,到時候別人拿著你的服務提供器,就能獲得例項化後的功能類物件了。
建立app\Providers\SktServiceProvider.php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Service\SktService;
class SktServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        //使用singleton繫結單例
        $this->app->singleton('skt',function(){
            return new SktService();
        });

        //使用bind繫結介面
        $this->app->bind('App\Contracts\SktContract',function(){
            return new SktService();
        });
    }
}

這了繫結了兩次,一次用singleton繫結的,一次用bind方法繫結的,區別是singleton繫結的是單例,繼續往下看就知道區別了。

4、既然服務提供器也準備好了,那就把提供器加到框架的載入列表中,之後就可以在控制器中使用啦。
config/app.php在providers鍵名對應的陣列中的最後,加入這行

App\Providers\SktServiceProvider::class,

5、接下來我們在控制器中使用我們剛剛自定義的服務

    public function index()
    {
        $fooService1 = app()->make('App\Contracts\SktContract'); //這裡產生的不是單例,這裡用bind方法繫結的
        $fooService2 = app()->make('App\Contracts\SktContract');

        $fooService3 = app()->make('skt'); //這裡產生的是單例,666,因為這裡skt是用singleton繫結的
        $fooService4 = app()->make('skt'); //這裡產生的是單例,666

        $fooService1->name='111';
        $fooService2->name='222';
        $fooService3->name='333';
        $fooService4->name='444';

        $fooService1->list_team('LOL-ssb'); //LOL-ssb的隊員名單是Faker-bengi
        var_dump($fooService1); //111
        var_dump($fooService2); //222
        var_dump($fooService3); //444
        var_dump($fooService4); //444
    }

通過app()->make('提供器中繫結時的鍵名');來獲取功能類物件,用bing方法繫結的,獲取出來的都是新物件,用singleton繫結的,獲取的是單例。

6、那麼問題來了,覺得這樣呼叫還是很麻煩,能不能再簡化呼叫?可以~ laravel有個facade功能。接下來我們建立一個檔案:/vendor/laravel/framework/src/Illuminate/Support/Facades/Skt.php

namespace Illuminate\Support\Facades;

use Illuminate\Contracts\Console\Kernel as ConsoleKernelContract;

/**
 * @method static bool list_team() //加上這行註釋是為了讓php_storm編輯器能夠追蹤函式
 * Class Skt
 * @package Illuminate\Support\Facades
 */
class Skt extends Facade  //類名隨便起,但是為了規範還是和功能類為主也叫Skt吧(功能類是叫SktService)
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor
    {
        return 'skt'; //這裡一定要return一個繫結的鍵名,代表你接下來會獲得那個鍵名繫結的物件
    }
}
上面這個類主要是getFacadeAccessor方法返回的字串要和你繫結時候的鍵名對應上,這裡返回的是skt,之前在服務提供器中,不是有一句
        $this->app->singleton('skt',function(){ //注意,這裡的鍵名是skt
            return new SktService();
        });

嗎,那麼返回的就是這個return new SktService();物件。

7、好了,Facades檔案也寫完了,接下來我們回到控制器中,把呼叫功能類的程式碼直接改為以下程式碼

use Illuminate\Support\Facades\Skt; //這裡要引入facades
class Test extends Controller
{
public function test()
    {
        Skt::list_team('LOL-ssb');
    }
}

這裡引入了 Illuminate\Support\Facades\Skt;獲得了一個叫做Skt的類,但是不好意思,這個類和你的功能類SktService只是名字有點像,但是完全沒有關係,起作用的是這個Skt類的getFacadeAccessor方法return了一個'skt'字串,然後框架內部獲取到這個'skt‘字串之後,又根據這個'skt'字串為鍵名,找到了之前通過singleton方法繫結的物件,那個物件才是真正的功能類。Skt::list_team()呼叫的實際上是SktService類的list_team()方法

總結:
介面類SktContract、功能類SktService、服務提供器SktServiceProvider(將功能類在裡面繫結好)、將服務提供器加入到框架載入列表config/app.php、建立facades檔案Skt(關鍵是renturn一個繫結時的鍵名)

實際上想要偷懶的話大可不必這麼麻煩,建立一個SktService.php功能類,然後再框架原有的AppServiceProvider裡面的register方法進行繫結也行,而且AppServiceProvider預設就在框架的載入列表中了,直接用就行,不過facades檔案還是要自己建立。這樣做的好處是省事,但是一旦各種各樣的功能都在AppServiceProvider類中進行繫結的話,就會很亂,不符合單一功能原則,最好還是一個功能類就建立一個服務提供器。不過要想引入功能類大可不必這麼麻煩呢,直接用trait,哪裡想用就use進來就行了,省事。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章