# 服務 —— 包開發
## 1、簡介
包是添加功能到Laravel的主要方式。包可以提供任何功能,小到處理日期如[Carbon](https://github.com/briannesbitt/Carbon),大到整個BDD測試框架如[Behat](https://github.com/Behat/Behat)。
當然,有很多不同類型的包。有些包是獨立的,意味著可以在任何框架中使用,而不僅是Laravel。比如Carbon和Behat都是獨立的包。所有這些包都可以通過在`composer.json`文件中請求以便被Laravel使用。
另一方面,其它包只能特定和Laravel一起使用,這些包可能有路由用于加強Laravel應用的功能,本章主要討論只能在Laravel中使用的包。
## 2、服務提供者
[服務提供者](http://laravelacademy.org/post/91.html)是包和Laravel之間的連接點。服務提供者負責綁定對象到Laravel的[服務容器](http://laravelacademy.org/post/93.html)并告知Laravel從哪里加載包資源如視圖、配置和本地化文件。
服務提供者繼承自`Illuminate\Support\ServiceProvider`類并包含兩個方法:`register`和`boot`。`ServiceProvider`基類位于Composer包`illuminate/support`。
要了解更多關于服務提供者的內容,查看其[文檔](http://laravelacademy.org/post/91.html)。
## 3、路由
要定義包的路由,只需要在包服務提供者中的`boot`方法中引入路由文件。在路由文件中,可以使用`Route`門面[注冊路由](http://laravelacademy.org/post/53.html),和Laravel應用中注冊路由一樣:
~~~
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot(){
if (! $this->app->routesAreCached()) {
require __DIR__.'/../../routes.php';
}
}
~~~
## 4、資源
### 4.1 視圖
要在Laravel中注冊包[視圖](http://laravelacademy.org/post/76.html),需要告訴Laravel視圖在哪,可以使用服務提供者的`loadViewsFrom`方法來實現。`loadViewsFrom`方法接收兩個參數:視圖模板的路徑和包名稱。例如,如果你的包名稱是“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');
});
~~~
### 4.1.1 覆蓋包視圖
當你使用`loadViewsFrom`方法的時候,Laravel實際上為視圖注冊了兩個存放位置:一個是`resources/views/vendor`目錄,另一個是你指定的目錄。所以,以`courier`為例:當請求一個包視圖時,Laravel首先檢查開發者是否在`resources/views/vendor/courier`提供了自定義版本的視圖,如果該視圖不存在,Laravel才會搜索你調用`loadViewsFrom`方法時指定的目錄。這種機制使得終端用戶可以輕松地自定義/覆蓋包視圖。
### 4.1.2?發布視圖
如果你想要視圖能夠發布到應用的`resources/views/vendor`目錄,可以使用服務提供者的`publishes`方法。該方法接收包視圖路徑及其相應的發布路徑數組作為參數:
~~~
/**
* 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'),
]);
}
~~~
現在,當包用戶執行Laravel的Artisan命令`vendor:publish`時,你的視圖包將會被拷貝到指定路徑。
### 4.2?翻譯
如果你的包包含[翻譯文件](http://laravelacademy.org/post/211.html),你可以使用`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');
~~~
### 4.3 配置
通常,你想要發布包配置文件到應用根目錄下的`config`目錄,這將允許包用戶輕松覆蓋默認配置選項,要發布一個配置文件,只需在服務提供者的`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'),
]);
}
~~~
現在,當包用戶執行Laravel的Artisan命令`vendor:publish`時,你的文件將會被拷貝到指定位置,當然,配置被發布后,可以通過和其它配置選項一樣的方式進行訪問:
~~~
$value = config('courier.option');
~~~
### 4.3.1 默認包配置
你還可以選擇將自己的包配置文件合并到應用的拷貝版本,這允許用戶只引入他們在應用配置文件中實際想要覆蓋的配置選項。要合并兩個配置,在服務提供者的`register`方法中使用`mergeConfigFrom`方法即可:
~~~
/**
* Register bindings in the container.
*
* @return void
*/
public function register(){
$this->mergeConfigFrom(
__DIR__.'/path/to/config/courier.php', 'courier'
);
}
~~~
## 5、公共前端資源
你的包可能包含JavaScript、CSS和圖片,要發布這些前端資源到應用根目錄下的`public`目錄,使用服務提供者的`publishes`方法。在本例中,我們添加一個前端資源組標簽`public`,用于發布相關的前端資源組:
~~~
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot(){
$this->publishes([
__DIR__.'/path/to/assets' => public_path('vendor/courier'),
], 'public');
}
~~~
現在,當包用戶執行`vendor:publish`命令時,前端資源將會被拷貝到指定位置,由于你需要在每次包更新時重寫前端資源,可以使用`--force`標識:
~~~
php artisan vendor:publish --tag=public --force
~~~
如果你想要確保前端資源已經更新到最新版本,可添加該命令到`composer.json`文件的`post-update-cmd`列表。
## 6、發布文件組
你可能想要分開發布包前端資源組和資源,例如,你可能想要用戶發布包配置的同時不發布包前端資源,可以通過在調用時給它們打上“標簽”來實現分離。下面我們在包服務提供者的`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');
}
~~~
現在用戶可以在使用Artisan命令`vendor:publish`時通過引用標簽名來分開發布這兩個組:
~~~
php artisan vendor:publish --provider="Vendor\Providers\PackageServiceProvider" --tag="config"
~~~
> 擴展閱讀:[實例教程 —— 如何在Laravel 5.1中進行自定義包開發](http://laravelacademy.org/post/1219.html)
- 前言
- 序言
- 序言 ―― 發行版本說明
- 序言 ―― 升級指南
- 序言 ―― 貢獻代碼
- 開始
- 開始 ―― 安裝及配置
- 開始 ―― Laravel Homestead
- 基礎
- 基礎 ―― HTTP路由
- 基礎 ―― HTTP 中間件
- 基礎 ―― HTTP 控制器
- 基礎 ―― HTTP 請求
- 基礎 ―― HTTP 響應
- 基礎 ―― 視圖
- 基礎 ―― Blade模板
- 架構
- 架構 ―― 一次請求的生命周期
- 架構 ―― 應用目錄結構
- 架構 ―― 服務提供者
- 架構 ―― 服務容器
- 架構 ―― 契約
- 架構 ―― 門面
- 數據庫
- 數據庫 ―― 起步
- 數據庫 ―― 查詢構建器
- 數據庫 ―― 遷移
- 數據庫 ―― 填充數據
- Eloquent ORM
- Eloquent ORM ―― 起步
- Eloquent ORM ―― 關聯關系
- Eloquent ORM ―― 集合
- Eloquent ORM ―― 調整器
- Eloquent ORM ―― 序列化
- 服務
- 服務 ―― 用戶認證
- 服務 ―― Artisan 控制臺
- 服務 ―― Laravel Cashier(交易)
- 服務 ―― 緩存
- 服務 ―― 集合
- 服務 ―― Laravel Elixir
- 服務 ―― 加密
- 服務 ―― 錯誤&日志
- 服務 ―― 事件
- 服務 ―― 文件系統/云存儲
- 服務 ―― 哈希
- 服務 ―― 幫助函數
- 服務 ―― 本地化
- 服務 ―― 郵件
- 服務 ―― 包開發
- 服務 ―― 分頁
- 服務 ―― 隊列
- 服務 ―― Redis
- 服務 ―― Session
- 服務 ―― Envoy 任務運行器(SSH任務)
- 服務 ―― 任務調度
- 服務 ―― 測試
- 服務 ―― 驗證