# 擴展包開發
- [簡介](#introduction)
- [Facades 注解](#a-note-on-facades)
- [發現擴展包](#package-discovery)
- [服務提供者](#service-providers)
- [資源文件](#resources)
- [配置](#configuration)
- [數據庫遷移](#migrations)
- [路由](#routes)
- [語言包](#translations)
- [視圖](#views)
- [命令](#commands)
- [公共資源文件](#public-assets)
- [發布群組文件](#publishing-file-groups)
<a name="introduction"></a>
## 簡介
擴展包是向 Laravel 中添加功能的主要方式。擴展包可以包含很多有用的功能,例如時間處理擴展包 [Carbon](https://github.com/briannesbitt/Carbon),或提供完整 BDD 測試框架的擴展包 [Behat](https://github.com/Behat/Behat)。
當然,擴展包有很多種類型。有些擴展包是獨立運行的,意味著他們可以在任意的 PHP 框架中使用。Carbon 和 Behat 就是這樣的獨立擴展包。要在 Laravel 中使用這種擴展包只需要在 `composer.json` 文件中引入他們即可。
另一方面,有些擴展包只能在 Laravel 中使用。這些擴展包可能包含專門用來增強 Laravel 應用的路由、控制器、視圖和配置的文件。這份指南主要介紹 Laravel 擴展包的開發。
<a name="a-note-on-facades"></a>
### Facades 注解
當開發 Laravel 應用時,通常使用契約(contracts)或 facades 沒有什么區別,因為他們都提供基本相同的可測試能力。然而,在進行擴展包開發的時候,擴展包并不能訪問 Laravel 提供的所有測試輔助函數。如果你想像在 Laravel 應用中一樣編寫擴展包的測試用例,你可以使用擴展包 [Orchestral Testbench](https://github.com/orchestral/testbench)。
<a name="package-discovery"></a>
## 發現擴展包
在 Laravel 應用的 `config/app.php` 配置文件中,`providers` 選項定義了能夠被 Laravel 加載的服務提供者列表。當有人安裝你的擴展包時,你需要將你的服務提供者包含到這個列表中。你可以將服務提供者定義到擴展包的 `composer.json` 文件中的 `extra` 部分,而不是讓用戶手動將你的服務提供者添加到列表中。除了服務提供者,還可以列出所有你想注冊的 [facades](/docs/{{version}}/facades):
"extra": {
"laravel": {
"providers": [
"Barryvdh\\Debugbar\\ServiceProvider"
],
"aliases": {
"Debugbar": "Barryvdh\\Debugbar\\Facade"
}
}
},
一旦你的擴展包配置為可發現,Laravel 就會在安裝時自動注冊擴展包的服務提供者和 facades,為擴展包的用戶提供一個友好的安裝體驗。
### 選擇性的發現擴展包
如果你是擴展包的用戶,想要禁止一個擴展包被發現,你可以在應用的 `composer.json` 文件中的 `extra` 部分列出這個擴展包:
"extra": {
"laravel": {
"dont-discover": [
"barryvdh/laravel-debugbar"
]
}
},
你也可以通過在應用的 `dont-discover` 指令中使用 `*` 字符,禁用擴展包發現功能:
"extra": {
"laravel": {
"dont-discover": [
"*"
]
}
},
<a name="service-providers"></a>
## 服務提供者
[服務提供者](/docs/{{version}}/providers) 讓你的擴展包與 Laravel 聯系在一起。服務提供者負責將一些東西綁定到 Laravel 的 [服務容器](/docs/{{version}}/container) 中,并且告訴 Laravel 從哪里加載擴展包的資源文件,例如視圖、配置文件、語言包等。
服務提供者繼承了 `Illuminate\Support\ServiceProvider` 類,并包含了兩個方法:`register` 和 `boot`。基類 `ServiceProvider` 位于名為 `illuminate/support` 的 Composer 擴展包中,你必須將它加入到你的擴展包依賴。想要了解更多關于服務提供者的結構和用途,請查閱 [它的文檔](/docs/{{version}}/providers).
<a name="resources"></a>
## 資源文件
<a name="configuration"></a>
### 配置
有時,你需要將擴展包配置文件發布到應用本身的 `config` 目錄中。這樣使用擴展包的用戶就可以輕松的重寫默認配置項。想要發布擴展包配置文件,只需要在服務提供者的 `boot` 方法中調用 `publishes` 方法即可:
/**
* 在注冊后啟動服務。
*
* @return void
*/
public function boot()
{
$this->publishes([
__DIR__.'/path/to/config/courier.php' => config_path('courier.php'),
]);
}
現在,當擴展包的用戶執行 Laravel 的 `vendor:publish` 命令,擴展包文件就會被復制到指定的目錄中。當然,一旦你的配置文件被發布,就可以如同其他配置一樣被訪問:
$value = config('courier.option');
> {note} 你不應該在配置文件中定義閉包函數。因為當用戶執行 `config:cache` Artisan 命令時,配置文件將不能被正確的序列化。
#### 擴展包默認配置
你也可以將擴展包默認配置與應用的副本配置合并在一起。這樣擴展包用戶就可以在副本配置文件中定義他們想要覆蓋的配置選項。想要合并配置,只需要在服務提供者的 `register` 方法中調用 `mergeConfigFrom` 方法即可:
/**
* 在容器中注冊綁定。
*
* @return void
*/
public function register()
{
$this->mergeConfigFrom(
__DIR__.'/path/to/config/courier.php', 'courier'
);
}
> {note} 此方法只合并配置數組的第一維。如果擴展包用戶定義了多維配置數組,缺少的選項將不會被合并。
<a name="routes"></a>
### 路由
如果你的擴展包中包含路由文件,你需要使用 `loadRoutesFrom` 方法加載他們。此方法將自動判斷應用的路由是否已被緩存,如果路由已緩存,將不會加載你的路由文件:
/**
* 在注冊后啟動服務。
*
* @return void
*/
public function boot()
{
$this->loadRoutesFrom(__DIR__.'/routes.php');
}
<a name="migrations"></a>
### 數據庫遷移
如果你的擴展包中包含 [數據庫遷移](/docs/{{version}}/migrations),你需要使用 `loadMigrationsFrom` 方法告知 Laravel 如何加載他們。`loadMigrationsFrom` 方法只需要擴展包遷移文件路徑作為唯一參數:
/**
* 在注冊后啟動服務。
*
* @return void
*/
public function boot()
{
$this->loadMigrationsFrom(__DIR__.'/path/to/migrations');
}
一旦你的擴展包遷移文件被注冊,當運行 `php artisan migrate` 命令時他們就會被自動執行。你不需要將他們導入到應用的 `database/migrations` 目錄中。
<a name="translations"></a>
### 語言包
如果你的擴展包中包含 [語言包文件](/docs/{{version}}/localization),你需要使用 `loadTranslationsFrom` 方法告知 Laravel 如何加載他們。例如,如果你的擴展包名為 `courier`,你需要將下面的內容加入到服務提供者的 `boot` 方法中:
/**
* 在注冊后啟動服務。
*
* @return void
*/
public function boot()
{
$this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'courier');
}
擴展包翻譯約定使用 `package::file.line` 語法進行引用。因此,你可以按照下面的方式來加載 `courier` 擴展包中的 `messages` 文件的 `welcome` 行:
echo trans('courier::messages.welcome');
#### 發布語言包
如果你想要將擴展包中的語言包發布到應用的 `resources/lang/vendor` 目錄,可以使用服務提供者的 `publishes` 方法。`publishes` 方法接收一個包含語言包路徑和對應發布位置的數組。例如,發布 `courier` 擴展包的語言包文件,操作如下:
/**
* 在注冊后啟動服務。
*
* @return void
*/
public function boot()
{
$this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'courier');
$this->publishes([
__DIR__.'/path/to/translations' => resource_path('lang/vendor/courier'),
]);
}
現在,當擴展包的用戶執行 Laravel 的 `vendor:publish` Artisan 命令,語言包將會被發布到指定的目錄中。
<a name="views"></a>
### 視圖
想要在 Laravel 中注冊你的擴展包的 [視圖](/docs/{{version}}/views), 需要告知 Laravel 視圖文件的位置。你可以使用服務提供者的 `loadViewsFrom` 方法來實現。`loadViewsFrom` 方法允許接收兩個參數:視圖模板路徑和擴展包名。例如,如果你的擴展包名為 `courier`,你需要將下面的內容加入到服務提供者的 `boot` 方法中:
/**
* 在注冊后啟動服務。
*
* @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 首先會檢查開發人員是否在 `resources/views/vendor/courier` 中提供了一個自定義版本的視圖。然后,如果視圖尚未被定義,Laravel 將會搜索在 `loadViewsFrom` 中定義的視圖目錄。這種方法可以讓用戶很簡單的自定義或重寫擴展包的視圖。
#### 發布視圖
如果你希望將你的視圖發布到應用的 `resources/views/vendor` 目錄中,可以使用服務提供者的 `publishes` 方法。`publishes` 方法接收一個包含視圖路徑和對應發布位置的數組:
/**
* 在注冊后啟動服務。
*
* @return void
*/
public function boot()
{
$this->loadViewsFrom(__DIR__.'/path/to/views', 'courier');
$this->publishes([
__DIR__.'/path/to/views' => resource_path('views/vendor/courier'),
]);
}
現在,當擴展包的用戶執行 Laravel 的 `vendor:publish` Artisan 命令,視圖將會被發布到自定的目錄中。
<a name="commands"></a>
## 命令
想要在 Laravel 中注冊擴展包的 Artisan 命令,需要使用 `commands` 方法。此方法接收一個命令類的數組。一旦這些命令注冊成功,可以使用 [Artisan 命令行](/docs/{{version}}/artisan) 執行他們:
/**
* 引導應用服務。
*
* @return void
*/
public function boot()
{
if ($this->app->runningInConsole()) {
$this->commands([
FooCommand::class,
BarCommand::class,
]);
}
}
<a name="public-assets"></a>
## 公共資源文件
你的擴展包中可能存在 JavaScript、CSS 和圖片之類的資源文件。想要發布這些資源文件到應用的 `public` 目錄,可以使用服務提供者的 `publishes` 方法。在下面的例子中,我們也可以添加一個 `public` 資源分類標簽,可用于相關發布資源的分類:
/**
* 在注冊后啟動服務。
*
* @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
<a name="publishing-file-groups"></a>
## 發布群組文件
你可能想要分別打包發布擴展包資源文件或資源。舉個例子,你想讓用戶單獨發布擴展包中的配置文件,而不是被強制發布擴展包中的所有資源文件。你可以通過調用擴展包服務提供者中的 `publishes` 方法給不同文件打上「標簽」。例如,讓我們使用擴展包服務提供者中的 `boot` 方法來定義兩個發布群組:
/**
* 在注冊后啟動服務。
*
* @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');
}
現在你的用戶就可以通過執行 `vendor:publish` 命令,根據定義的標簽來發布不同的群組文件:
php artisan vendor:publish --tag=config
- 入門指南
- 安裝
- 部署
- 基礎功能
- 路由
- 中間件
- CSRF 保護
- 控制器
- 請求
- 響應
- 視圖
- URL
- Session
- 表單驗證
- 錯誤
- 日志
- 前端開發
- Blade 模板
- 本地化
- 腳手架
- 編譯資源 Mix
- 安全相關
- 用戶認證
- API 認證
- 綜合話題
- 命令行
- 廣播
- 緩存
- 集合
- 事件
- 文件存儲
- 輔助函數
- 郵件發送
- 消息通知
- 擴展包開發
- 隊列
- 任務調度
- 數據庫
- 快速入門
- 查詢構造器
- 分頁
- 數據庫遷移
- 數據填充
- Redis
- Eloquent ORM
- 快速入門
- 速查表
- Artisan
- Auth
- Blade
- Cache
- Collection
- Composer
- Config
- Container
- Cookie
- DB
- Environment
- Event
- File
- Helper
- Input
- Lang
- Log
- Model
- Pagination
- Queue
- Redirect
- Request
- Response
- Route
- SSH
- Schema
- Security
- Session
- Storage
- String
- URL
- UnitTest
- Validation
- View