跳轉至內容

包開發

簡介

包是向 Laravel 新增功能的主要方式。包可以是各種各樣的東西,例如處理日期的出色工具 Carbon,或者是允許將檔案關聯到 Eloquent 模型的包,如 Spatie 的 Laravel Media Library

包有不同的型別。有些包是獨立的,意味著它們可以與任何 PHP 框架配合使用。Carbon 和 Pest 就是獨立包的示例。透過在 composer.json 檔案中引用這些包,可以在 Laravel 中使用它們。

另一方面,其他包則是專門為 Laravel 設計的。這些包可能包含專門用於增強 Laravel 應用程式的路由、控制器、檢視和配置。本指南主要介紹這些特定於 Laravel 的包的開發。

關於 Facades 的說明

在編寫 Laravel 應用程式時,使用契約(contracts)還是門面(facades)通常並不重要,因為兩者提供了基本相同的可測試性級別。但是,在編寫包時,您的包通常無法訪問所有 Laravel 的測試輔助函式。如果您希望像在典型的 Laravel 應用程式中安裝包那樣編寫包測試,可以使用 Orchestral Testbench 包。

包發現

Laravel 應用程式的 bootstrap/providers.php 檔案包含應由 Laravel 載入的服務提供者列表。但是,與其要求使用者手動將您的服務提供者新增到列表中,不如在包的 composer.json 檔案的 extra 部分中定義提供者,以便 Laravel 自動載入它。除了服務提供者,您還可以列出您希望註冊的任何 門面(facades)

1"extra": {
2 "laravel": {
3 "providers": [
4 "Barryvdh\\Debugbar\\ServiceProvider"
5 ],
6 "aliases": {
7 "Debugbar": "Barryvdh\\Debugbar\\Facade"
8 }
9 }
10},

一旦您的包配置了包發現功能,Laravel 在安裝時會自動註冊其服務提供者和門面,從而為您的包使用者創造便捷的安裝體驗。

停用包發現

如果您是某個包的使用者,並希望停用該包的發現功能,可以在應用程式的 composer.json 檔案的 extra 部分中列出該包的名稱。

1"extra": {
2 "laravel": {
3 "dont-discover": [
4 "barryvdh/laravel-debugbar"
5 ]
6 }
7},

您可以使用 * 字元在應用程式的 dont-discover 指令中停用所有包的自動發現。

1"extra": {
2 "laravel": {
3 "dont-discover": [
4 "*"
5 ]
6 }
7},

服務提供者

服務提供者 是您的包與 Laravel 之間的連線點。服務提供者負責將內容繫結到 Laravel 的 服務容器 中,並告知 Laravel 在何處載入包資源,如檢視、配置和語言檔案。

服務提供者繼承 Illuminate\Support\ServiceProvider 類,幷包含兩個方法:registerboot。基礎 ServiceProvider 類位於 illuminate/support Composer 包中,您應該將其新增到自己包的依賴項中。要了解有關服務提供者結構和目的的更多資訊,請檢視 其文件

資源

配置

通常,您需要將包的配置檔案釋出到應用程式的 config 目錄中。這將允許您的包的使用者輕鬆覆蓋預設配置選項。要允許釋出配置檔案,請在服務提供者的 boot 方法中呼叫 publishes 方法。

1/**
2 * Bootstrap any package services.
3 */
4public function boot(): void
5{
6 $this->publishes([
7 __DIR__.'/../config/courier.php' => config_path('courier.php'),
8 ]);
9}

現在,當您的包的使用者執行 Laravel 的 vendor:publish 命令時,您的檔案將被複制到指定的釋出位置。一旦配置被髮布,就可以像訪問其他配置檔案一樣訪問其值。

1$value = config('courier.option');

您不應在配置檔案中定義閉包(closures)。當用戶執行 config:cache Artisan 命令時,它們無法正確序列化。

預設包配置

您還可以將自己的包配置檔案與應用程式的已釋出副本合併。這將允許使用者僅定義他們實際上想要在已釋出配置檔案副本中覆蓋的選項。要合併配置檔案值,請在服務提供者的 register 方法中使用 mergeConfigFrom 方法。

mergeConfigFrom 方法的第一個引數是包配置檔案的路徑,第二個引數是應用程式中配置檔案的名稱。

1/**
2 * Register any package services.
3 */
4public function register(): void
5{
6 $this->mergeConfigFrom(
7 __DIR__.'/../config/courier.php', 'courier'
8 );
9}

此方法僅合併配置陣列的第一層。如果使用者部分定義了多維配置陣列,缺失的選項將不會被合併。

路由

如果您的包包含路由,您可以使用 loadRoutesFrom 方法載入它們。此方法將自動確定應用程式的路由是否已快取,如果路由已被快取,則不會載入您的路由檔案。

1/**
2 * Bootstrap any package services.
3 */
4public function boot(): void
5{
6 $this->loadRoutesFrom(__DIR__.'/../routes/web.php');
7}

遷移

如果您的包包含 資料庫遷移,您可以使用 publishesMigrations 方法告知 Laravel 指定目錄或檔案包含遷移。當 Laravel 釋出遷移時,它會自動更新檔名中的時間戳以反映當前的日期和時間。

1/**
2 * Bootstrap any package services.
3 */
4public function boot(): void
5{
6 $this->publishesMigrations([
7 __DIR__.'/../database/migrations' => database_path('migrations'),
8 ]);
9}

語言檔案

如果您的包包含 語言檔案,您可以使用 loadTranslationsFrom 方法告知 Laravel 如何載入它們。例如,如果您的包名為 courier,您應該將以下內容新增到服務提供者的 boot 方法中:

1/**
2 * Bootstrap any package services.
3 */
4public function boot(): void
5{
6 $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier');
7}

包翻譯行使用 package::file.line 語法約定進行引用。因此,您可以按照以下方式從 messages 檔案載入 courier 包的 welcome 行:

1echo trans('courier::messages.welcome');

您可以使用 loadJsonTranslationsFrom 方法為您的包註冊 JSON 翻譯檔案。此方法接受包含包 JSON 翻譯檔案的目錄路徑。

1/**
2 * Bootstrap any package services.
3 */
4public function boot(): void
5{
6 $this->loadJsonTranslationsFrom(__DIR__.'/../lang');
7}

釋出語言檔案

如果您希望將包的語言檔案釋出到應用程式的 lang/vendor 目錄,可以使用服務提供者的 publishes 方法。publishes 方法接受一個包含包路徑及其期望釋出位置的陣列。例如,要釋出 courier 包的語言檔案,您可以執行以下操作:

1/**
2 * Bootstrap any package services.
3 */
4public function boot(): void
5{
6 $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier');
7 
8 $this->publishes([
9 __DIR__.'/../lang' => $this->app->langPath('vendor/courier'),
10 ]);
11}

現在,當您的包的使用者執行 Laravel 的 vendor:publish Artisan 命令時,您的包語言檔案將被髮布到指定的釋出位置。

檢視

要將包的 檢視 註冊到 Laravel,您需要告知 Laravel 檢視所在的位置。您可以使用服務提供者的 loadViewsFrom 方法來完成此操作。loadViewsFrom 方法接受兩個引數:檢視模板的路徑和包的名稱。例如,如果包名稱為 courier,您將在服務提供者的 boot 方法中新增以下內容:

1/**
2 * Bootstrap any package services.
3 */
4public function boot(): void
5{
6 $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');
7}

包檢視使用 package::view 語法約定進行引用。因此,一旦檢視路徑在服務提供者中註冊,您就可以按照以下方式從 courier 包載入 dashboard 檢視:

1Route::get('/dashboard', function () {
2 return view('courier::dashboard');
3});

覆蓋包檢視

當使用 loadViewsFrom 方法時,Laravel 實際上為您的檢視註冊了兩個位置:應用程式的 resources/views/vendor 目錄和您指定的目錄。因此,以 courier 包為例,Laravel 將首先檢查開發人員是否在 resources/views/vendor/courier 目錄中放置了該檢視的自定義版本。如果沒有自定義檢視,Laravel 將搜尋您在呼叫 loadViewsFrom 時指定的包檢視目錄。這使得包使用者可以輕鬆自定義/覆蓋您的包檢視。

釋出檢視

如果您希望使檢視可釋出到應用程式的 resources/views/vendor 目錄,可以使用服務提供者的 publishes 方法。publishes 方法接受一個包含包檢視路徑及其期望釋出位置的陣列。

1/**
2 * Bootstrap the package services.
3 */
4public function boot(): void
5{
6 $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');
7 
8 $this->publishes([
9 __DIR__.'/../resources/views' => resource_path('views/vendor/courier'),
10 ]);
11}

現在,當您的包的使用者執行 Laravel 的 vendor:publish Artisan 命令時,您的包檢視將被複制到指定的釋出位置。

檢視元件

如果您正在構建利用 Blade 元件或將元件放置在非常規目錄中的包,則需要手動註冊元件類及其 HTML 標籤別名,以便 Laravel 知道在哪裡查詢元件。通常,您應該在包服務提供者的 boot 方法中註冊元件:

1use Illuminate\Support\Facades\Blade;
2use VendorPackage\View\Components\AlertComponent;
3 
4/**
5 * Bootstrap your package's services.
6 */
7public function boot(): void
8{
9 Blade::component('package-alert', AlertComponent::class);
10}

一旦元件註冊完成,就可以使用其標籤別名進行渲染:

1<x-package-alert/>

自動載入包元件

或者,您可以使用 componentNamespace 方法按約定自動載入元件類。例如,Nightshade 包可能擁有位於 Nightshade\Views\Components 名稱空間內的 CalendarColorPicker 元件:

1use Illuminate\Support\Facades\Blade;
2 
3/**
4 * Bootstrap your package's services.
5 */
6public function boot(): void
7{
8 Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
9}

這將允許使用 package-name:: 語法按其供應商名稱空間使用包元件:

1<x-nightshade::calendar />
2<x-nightshade::color-picker />

Blade 將透過將元件名稱轉為帕斯卡命名法(Pascal-case)自動檢測連結到此元件的類。子目錄也支援使用“點”符號。

匿名元件

如果您的包包含匿名元件,它們必須放置在包“檢視”目錄(由 loadViewsFrom 方法 指定)的 components 目錄中。然後,您可以透過在元件名稱前加上包的檢視名稱空間來渲染它們:

1<x-courier::alert />

“About” Artisan 命令

Laravel 內建的 about Artisan 命令提供了應用程式環境和配置的摘要。包可以透過 AboutCommand 類將附加資訊推送到此命令的輸出中。通常,此資訊可以從包服務提供者的 boot 方法中新增:

1use Illuminate\Foundation\Console\AboutCommand;
2 
3/**
4 * Bootstrap any package services.
5 */
6public function boot(): void
7{
8 AboutCommand::add('My Package', fn () => ['Version' => '1.0.0']);
9}

命令

要將包的 Artisan 命令註冊到 Laravel,可以使用 commands 方法。此方法需要一個包含命令類名稱的陣列。命令註冊後,您可以使用 Artisan CLI 執行它們:

1use Courier\Console\Commands\InstallCommand;
2use Courier\Console\Commands\NetworkCommand;
3 
4/**
5 * Bootstrap any package services.
6 */
7public function boot(): void
8{
9 if ($this->app->runningInConsole()) {
10 $this->commands([
11 InstallCommand::class,
12 NetworkCommand::class,
13 ]);
14 }
15}

最佳化命令

Laravel 的 最佳化命令 會快取應用程式的配置、事件、路由和檢視。使用 optimizes 方法,您可以註冊當執行 optimizeoptimize:clear 命令時應呼叫的包專屬 Artisan 命令:

1/**
2 * Bootstrap any package services.
3 */
4public function boot(): void
5{
6 if ($this->app->runningInConsole()) {
7 $this->optimizes(
8 optimize: 'package:optimize',
9 clear: 'package:clear-optimizations',
10 );
11 }
12}

過載命令

Laravel 的 過載命令 會終止任何正在執行的服務,以便系統程序監視器可以自動重啟它們。使用 reloads 方法,您可以註冊當執行 reload 命令時應呼叫的包專屬 Artisan 命令:

1/**
2 * Bootstrap any package services.
3 */
4public function boot(): void
5{
6 if ($this->app->runningInConsole()) {
7 $this->reloads('package:reload');
8 }
9}

公共資源

您的包可能包含 JavaScript、CSS 和圖片等資源。要將這些資源釋出到應用程式的 public 目錄,請使用服務提供者的 publishes 方法。在此示例中,我們還將新增一個 public 資源組標籤,用於輕鬆釋出相關資源組:

1/**
2 * Bootstrap any package services.
3 */
4public function boot(): void
5{
6 $this->publishes([
7 __DIR__.'/../public' => public_path('vendor/courier'),
8 ], 'public');
9}

現在,當包使用者執行 vendor:publish 命令時,您的資源將被複制到指定的釋出位置。由於使用者通常需要在每次更新包時覆蓋資源,他們可以使用 --force 標誌。

1php artisan vendor:publish --tag=public --force

釋出檔案組

您可能希望分別釋出包資源和資源的組。例如,您可能希望允許使用者釋出包配置檔案,而不強制釋出包資源。您可以透過在從包服務提供者呼叫 publishes 方法時對它們進行“標記(tagging)”來實現這一點。例如,讓我們在包服務提供者的 boot 方法中使用標籤為 courier 包定義兩個釋出組(courier-configcourier-migrations):

1/**
2 * Bootstrap any package services.
3 */
4public function boot(): void
5{
6 $this->publishes([
7 __DIR__.'/../config/package.php' => config_path('package.php')
8 ], 'courier-config');
9 
10 $this->publishesMigrations([
11 __DIR__.'/../database/migrations/' => database_path('migrations')
12 ], 'courier-migrations');
13}

現在,您的使用者可以在執行 vendor:publish 命令時透過引用其標籤來分別釋出這些組:

1php artisan vendor:publish --tag=courier-config

使用者還可以使用 --provider 標誌釋出由包的服務提供者定義的所有可釋出檔案:

1php artisan vendor:publish --provider="Your\Package\ServiceProvider"