包是添加功能到 Laravel 的主要方式。包可以提供任何功能,小到處理日期如 Carbon,大到整個 BDD 測試框架如 Behat。
當然,有很多不同類型的包。有些包是獨立的,意味著可以在任何框架中使用,而不僅是 Laravel。比如 Carbon 和 Behat 都是獨立的包。所有這些包都可以通過在 composer.json
文件中請求以便被 Laravel 使用。
另一方面,其它包只能特定和 Laravel 一起使用,這些包可能有路由,控制器、視圖和配置用于加強 Laravel 應用的功能,本章主要討論只能在 Laravel 中使用的包。
服務提供者是包和 Laravel 之間的連接點。服務提供者負責綁定對象到 Laravel 的服務容器并告知 Laravel 從哪里加載包資源如視圖、配置和本地化文件。
服務提供者繼承自 Illuminate\Support\ServiceProvider
類并包含兩個方法:register
和 boot
。ServiceProvider
基類位于 Composer 包 illuminate/support
。
要了解更多關(guān)于服務提供者的內(nèi)容,查看其文檔。
要定義包的路由,只需要在包服務提供者中的 boot
方法中引入路由文件。在路由文件中,可以使用 Route
門面注冊路由,和 Laravel 應用中注冊路由一樣:
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot(){
if (! $this->app->routesAreCached()) {
require __DIR__.'/../../routes.php';
}
}
要在 Laravel 中注冊包視圖,需要告訴 Laravel 視圖在哪,可以使用服務提供者的 loadViewsFrom
方法來實現(xiàn)。loadViewsFrom
方法接收兩個參數(shù):視圖模板的路徑和包名稱。例如,如果你的包名稱是“courier”,添加如下代碼到服務提供者的 boot
方法:
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot(){
$this->loadViewsFrom(__DIR__.'/path/to/views', 'courier');
}
包視圖通過使用類似的 package::view
語法來引用。所以,你可以通過如下方式加載courier
包上的 admin
視圖:
Route::get('admin', function () {
return view('courier::admin');
});
當你使用 loadViewsFrom
方法的時候,Laravel 實際上為視圖注冊了兩個存放位置:一個是 resources/views/vendor
目錄,另一個是你指定的目錄。所以,以 courier
為例:當請求一個包視圖時,Laravel 首先檢查開發(fā)者是否在 resources/views/vendor/courier
提供了自定義版本的視圖,如果該視圖不存在,Laravel 才會搜索你調(diào)用 loadViewsFrom
方法時指定的目錄。這種機制使得終端用戶可以輕松地自定義/覆蓋包視圖。
如果你想要視圖能夠發(fā)布到應用的 resources/views/vendor
目錄,可以使用服務提供者的publishes
方法。該方法接收包視圖路徑及其相應的發(fā)布路徑數(shù)組作為參數(shù):
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot(){
$this->loadViewsFrom(__DIR__.'/path/to/views', 'courier');
$this->publishes([
__DIR__.'/path/to/views' => base_path('resources/views/vendor/courier'),
]);
}
現(xiàn)在,當包用戶執(zhí)行 Laravel 的 Artisan 命令 vendor:publish
時,你的視圖包將會被拷貝到指定路徑。
如果你的包包含翻譯文件,你可以使用 loadTranslationsFrom
方法告訴 Laravel 如何加載它們,例如,如果你的包命名為“courier”,你應該添加如下代碼到服務提供者的 boot
方法:
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot(){
$this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'courier');
}
包翻譯使用形如 package::file.line
的語法進行引用。所以,你可以使用如下方式從messages
文件中加載 courier
包的 welcome
行:
echo trans('courier::messages.welcome');
通常,你想要發(fā)布包配置文件到應用根目錄下的 config
目錄,這將允許包用戶輕松覆蓋默認配置選項,要發(fā)布一個配置文件,只需在服務提供者的 boot
方法中使用 publishes
方法即可:
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot(){
$this->publishes([
__DIR__.'/path/to/config/courier.php' => config_path('courier.php'),
])
}
現(xiàn)在,當包用戶執(zhí)行 Laravel 的 Artisan 命令 vendor:publish
時,你的文件將會被拷貝到指定位置,當然,配置被發(fā)布后,可以通過和其它配置選項一樣的方式進行訪問:
$value = config('courier.option');
你還可以選擇將自己的包配置文件合并到應用的拷貝版本,這允許用戶只引入他們在應用配置文件中實際想要覆蓋的配置選項。要合并兩個配置,在服務提供者的 register
方法中使用 mergeConfigFrom
方法即可:
/**
* Register bindings in the container.
*
* @return void
*/
public function register(){
$this->mergeConfigFrom(
__DIR__.'/path/to/config/courier.php', 'courier'
);
}
你的包可能包含 JavaScript、CSS 和圖片,要發(fā)布這些前端資源到應用根目錄下的 public
目錄,使用服務提供者的 publishes
方法。在本例中,我們添加一個前端資源組標簽 public
,用于發(fā)布相關(guān)的前端資源組:
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot(){
$this->publishes([
__DIR__.'/path/to/assets' => public_path('vendor/courier'),
], 'public');
}
現(xiàn)在,當包用戶執(zhí)行 vendor:publish
命令時,前端資源將會被拷貝到指定位置,由于你需要在每次包更新時重寫前端資源,可以使用--force
標識:
php artisan vendor:publish --tag=public --force
如果你想要確保前端資源已經(jīng)更新到最新版本,可添加該命令到composer.json
文件的post-update-cmd
列表。
你可能想要發(fā)分開布包前端資源組和資源,例如,你可能想要用戶發(fā)布包配置的同時不發(fā)布包前端資源,可以通過在調(diào)用時給它們打上“標簽”來實現(xiàn)分離。下面我們在包服務提供者的 boot
方法中定義兩個公共組:
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot(){
$this->publishes([
__DIR__.'/../config/package.php' => config_path('package.php')
], 'config');
$this->publishes([
__DIR__.'/../database/migrations/' => database_path('migrations')
], 'migrations');
}
現(xiàn)在用戶可以在使用 Artisan 命令 vendor:publish
時通過引用標簽名來分開發(fā)布這兩個組:
php artisan vendor:publish --provider="Vendor\Providers\PackageServiceProvider" --tag="config"