這篇會說明 Laravel5 在套件開發上,套件的 view 如何與 Laravel 整合、套件的 config、migrations 或其他 assets 如何發佈到 Laravel 上,還有語言如何與 Laravel 整合。

在我這篇 Laravel 5 沒有 workbench 的套件開發 裡面有說明了如何在沒有 workbench 下開發套件。

套件架構

假使我們的套件目錄內結構如下,且套件名稱取名為 courier:

config/
  courier.php
lang/
  en/
  zh-TW/
    courier.php
migrations/
src/
  CourierServiceProvider.php
tests/
views/
  layouts/
    interface.blade.php
  partials/
    notifaction.blade.php
composer.json

目錄的結構可以依照每個套件的需求自行設定。只要 autoloading 與 namespace 設定好即可

下方的說明會依照這個架構來舉例

Service Provider

套件裡的 Service Provider 是開發套件後與 Laravel 串接起來重要的部分。套件裡可以有一個或多個以上的 Service Provider。

在我們套件裡的 src/CourierServiceProvider.php 這個檔案有兩個主要的 method,分別是 register 和 boot。

<?php namespace Foo;

use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider {

    public function register()
    {
        //
    }

    public function boot()
    {
        //
    }

}

register method 主要是負責物件的綁定。這篇不會說明到這個部分。我們要說明的是套件的設定,也就是主要在 boot 這個 method 裡的設定。

php artisan vendor:publish 指令

在介紹設定之前,先說明一下指令的部份。Laravel5 已經沒有 Laravel4 的 php artisan asset:publish 或 php artisan config:publish 這些指令了。在 Laravel5 只有一個發佈的相關指令。這個指令會發佈所有已經在 config/app.php 裡註冊的 service provider 裡的所有設定。

php artisan vendor:publish
指令的相關參數
  • 加上 --tag="config" 可以只發佈我們取名為 config 標籤的 publishes
  • 加上 --provider="Foo\CourierServiceProvider" 可以只發佈這一個 service provider 所設定的 publishes
  • 加上 --force 會強迫覆蓋已存在的檔案

設定檔的發佈與合併

下方這個設定是說,把我這個 service provider 檔案所在目錄上一層 config 目錄裡的 courier.php 檔案複製到 Laravel 的 config 目錄裡,且檔案取名為 courier.php。且給這一個 publishes 取一個名為 config 的標籤。

public function boot()
{
    $this->publishes([
        __DIR__.'/../config/courier.php' => config_path('courier.php')
    ], 'config');
}

publishes 的第一個參數是一個陣列,可以發佈多筆檔案。

如果不需要設定標籤,把 publishes 的第二個參數拿掉就可以了。

當我執行 php artisan vendor:publish --tag="config" 後可以將設定了這個標籤的 publishes 發佈出去到指定的位置。

當我們在套件裡建立了套件的設定檔後,如 config/courier.php。首先我們可以將這個設定檔發佈到指定的路徑去。當然套件的使用者可以修正覆寫這個設定檔。如下的設定可以合併套件自己本身的 courier.php 和套件使用者自行設定的 courier.php。

public function boot()
{
    $this->mergeConfigFrom(
        __DIR__.'/../config/courier.php', 'courier'
    );
}

有幾點要注意:

  • 這一個 mergeConfigFrom 只可以合併 config 目錄下的設定檔而已。其他的檔案無法。
  • 如果沒有將 config 發佈出去。使用 config('courier') 會取得套件裡的 config,前提是要有定義 mergeConfigFrom 這個 method。反之如果沒有將 config 發佈,也沒有定義 mergeConfigFrom 這個 method,使用 config('courier') 會載不到資料。

其他資源的發佈

在 Laravel5 可以發佈任何想發佈的檔案到任何地方,例如 migrations 或其他 assets。設定方式一樣使用 publishes method。

public function boot()
{
    $this->publishes([
        __DIR__.'/../migrations' => base_path('/database/migrations'),
    ], 'migration');
}

這是將 migrations 整個目錄裡的所有檔案複製到 Laravel base 目錄下 database/migrations 裡面,且將這個 publishes 取名標籤為 migration。可以使用 --tag="migration" 參數只發佈這個 publishes。

Views

在我們套件的目錄裡有一個 views 目錄,裡面放置了這個套件所需要使用到的 view 檔案。如果我們想要把這個目錄告訴 Laravel,跟他說我這個套件的 view 目錄在這裡,如果要使用請來這裡載入。

public function boot()
{
    $this->loadViewsFrom(__DIR__ . '/../views', 'courier');
}

上方這個設定告訴了 Laravel 我的 view 在這個 serive provider 所在目錄的上一層 views 目錄裡,且取名為 courier 接下來就可以使用 view('courier::layouts.interface') 來取用了。

雙引號前面是我們為這個套件所設定的名稱

如果沒有設定這行 loadViewsFrom,在套件裡想用使用 view 是沒有作用的。當然定義 loadViewsFrom 後,使用了這個套件的使用者才可以使用或甚至是覆寫。

如果套件使用者想要覆寫可以在 resources/views/vendor/courier 裡依照相對的路徑覆寫。例如套件裡有一個 views/layouts/interface.blade.php 檔案,且套件使用者想要覆寫這個檔案,可以在 resources/views/vendor/courier/layouts 裡建立 interface.blade.php 這個檔案。

套件語言

我們的套件裡有一個 lang 目錄,且裡面有各個語言版本的目錄(en、zh-TW),如果想要告訴 Laravel,跟他說我這個套件的語言在哪裡,如果需要使用的時候請來我設定的地方載入。設定方式如下:

public function boot()
{
    $this->loadTranslationsFrom(__DIR__ . '/../lang', 'courier');
}

這個設定告訴了 Laravel 我的語言在這個 serive provider 檔案上一層的 lang 目錄裡,且取名為 courier。接下來我們就可以使用 trans('courier::coruier.name') 來取得語言了

雙引號前面是我們為這個套件所設定的名稱

注意: lang 語言目錄裡一定要有各個語言版本更下一層目錄。例如我們的套件架構。

Illuminate\Support\ServiceProvider

我們每一個 service provider 都繼承Illuminate\Support\ServiceProvider 這個抽象類別。

我們更進一步來討論一下這個類別。首先我們先看一下 loadViewsFrom 和 loadTranslationsFrom 這兩個 method:

protected function loadViewsFrom($path, $namespace)
{
    if (is_dir($appPath = $this->app->basePath().'/resources/views/vendor/'.$namespace))
    {
        $this->app['view']->addNamespace($namespace, $appPath);
    }

    $this->app['view']->addNamespace($namespace, $path);
}

protected function loadTranslationsFrom($path, $namespace)
{
    $this->app['translator']->addNamespace($namespace, $path);
}

我們可以發現 loadViewsFrom 這一個 method 多了一塊判斷 resources/views/vendor 下相關的目錄存不存在(以這個例子來說是courier)。但 loadTranslationsFrom 則沒有。這樣就可以知道,view是可以在 resources/views/vendor 下依照設定所覆寫,但語言不可以。

另外還有一個 method 是 pathsToPublish 就是我們使用 php artisan vendor:publish 指令時,加上 --provider 或 --tag 時會執行的 method。

最後要介紹的還有一個 commands 這一個 method。使用這個 method 註冊套件本身的指令是一個好方法。

加入 app.php

最後記得將 Service Provider 加入到 config/app.php 設定檔裡的 providers 陣列裡去。讓 Laravel 開始負責處理您的套件了。