パッケージ開発
イントロダクション
パッケージは、Laravelに機能を追加するための主要な方法です。パッケージには、Carbonのように日付を扱うのに最適な方法から、SpatieのLaravel Media LibraryのようにEloquentモデルにファイルを関連付けることができるパッケージまで、あらゆるものがあります。
パッケージにはさまざまな種類があります。一部のパッケージはスタンドアロンであり、どのPHPフレームワークでも動作します。CarbonとPestはスタンドアロンパッケージの例です。これらのパッケージは、composer.jsonファイルで必須指定すれば、Laravelで使用できます。
一方、Laravelでの使用を特に意図したパッケージもあります。これらのパッケージには、Laravelアプリケーションを強化することを特に意図したルート、コントローラ、ビュー、および設定が含まれる場合があります。このガイドでは、主にLaravel固有のパッケージの開発について説明します。
ファサードについての注意
Laravelアプリケーションを作成する際には、契約とファサードのどちらを使用するかは一般的に問題になりません。どちらも本質的に同等のテスト容易性を提供するためです。しかし、パッケージを作成する場合、通常、パッケージはLaravelのすべてのテストヘルパーにアクセスできません。パッケージが典型的なLaravelアプリケーション内にインストールされているかのようにパッケージテストを作成したい場合は、Orchestral Testbenchパッケージを使用できます。
パッケージディスカバリ
Laravelアプリケーションのbootstrap/providers.phpファイルには、Laravelによってロードされるべきサービスプロバイダのリストが含まれています。しかし、ユーザーにサービスプロバイダを手動でリストに追加してもらう代わりに、パッケージのcomposer.jsonファイルのextraセクションでプロバイダを定義し、Laravelによって自動的にロードされるようにすることができます。サービスプロバイダに加えて、登録したいファサードをリストすることもできます。
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クラスを拡張し、registerとbootの2つのメソッドを含みます。ベースのServiceProviderクラスはilluminate/support Composerパッケージにあり、これを自分のパッケージの依存関係に追加する必要があります。サービスプロバイダの構造と目的について詳しく知るには、そのドキュメントを確認してください。
リソース
設定
通常、パッケージの設定ファイルをアプリケーションのconfigディレクトリに公開する必要があります。これにより、パッケージのユーザーがデフォルトの設定オプションを簡単に上書きできるようになります。設定ファイルを公開可能にするには、サービスプロバイダのbootメソッドからpublishesメソッドを呼び出します。
1/**2 * Bootstrap any package services.3 */4public function boot(): void5{6 $this->publishes([7 __DIR__.'/../config/courier.php' => config_path('courier.php'),8 ]);9}
これで、パッケージのユーザーがLaravelのvendor:publishコマンドを実行すると、あなたのファイルは指定された公開場所にコピーされます。設定が公開されると、その値は他の設定ファイルと同様にアクセスできます。
1$value = config('courier.option');
設定ファイルにクロージャを定義してはいけません。ユーザーがconfig:cache Artisanコマンドを実行したときに、正しくシリアライズできません。
デフォルトのパッケージ設定
独自のパッケージ設定ファイルを、アプリケーションで公開されたコピーとマージすることもできます。これにより、ユーザーは設定ファイルの公開されたコピーで、実際に上書きしたいオプションのみを定義できます。設定ファイルの値をマージするには、サービスプロバイダのregisterメソッド内でmergeConfigFromメソッドを使用します。
mergeConfigFromメソッドは、第1引数にパッケージの設定ファイルへのパスを、第2引数にアプリケーションのコピーである設定ファイルの名前を受け入れます。
1/**2 * Register any application services.3 */4public function register(): void5{6 $this->mergeConfigFrom(7 __DIR__.'/../config/courier.php', 'courier'8 );9}
このメソッドは、設定配列の第1レベルのみをマージします。ユーザーが多次元の設定配列を部分的に定義した場合、不足しているオプションはマージされません。
ルート
パッケージにルートが含まれている場合は、loadRoutesFromメソッドを使用してそれらをロードできます。このメソッドは、アプリケーションのルートがキャッシュされているかどうかを自動的に判断し、ルートがすでにキャッシュされている場合はルートファイルをロードしません。
1/**2 * Bootstrap any package services.3 */4public function boot(): void5{6 $this->loadRoutesFrom(__DIR__.'/../routes/web.php');7}
マイグレーション
パッケージにデータベースマイグレーションが含まれている場合、publishesMigrationsメソッドを使用して、指定されたディレクトリまたはファイルにマイグレーションが含まれていることをLaravelに通知できます。Laravelがマイグレーションを公開すると、ファイル名内のタイムスタンプが現在の日時を反映するように自動的に更新されます。
1/**2 * Bootstrap any package services.3 */4public function boot(): void5{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(): void5{6 $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier');7}
パッケージの翻訳行は、package::file.lineという構文規則を使用して参照されます。したがって、courierパッケージのmessagesファイルからwelcome行を次のようにロードできます。
1echo trans('courier::messages.welcome');
loadJsonTranslationsFromメソッドを使用して、パッケージのJSON翻訳ファイルを登録できます。このメソッドは、パッケージのJSON翻訳ファイルが含まれるディレクトリへのパスを受け入れます。
1/**2 * Bootstrap any package services.3 */4public function boot(): void5{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メソッドは2つの引数を受け入れます。ビューテンプレートへのパスとパッケージの名前です。たとえば、パッケージ名がcourierの場合、サービスプロバイダのbootメソッドに以下を追加します。
1/**2 * Bootstrap any package services.3 */4public function boot(): void5{6 $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');7}
パッケージのビューは、package::viewという構文規則を使用して参照されます。したがって、サービスプロバイダでビューパスが登録されると、courierパッケージからdashboardビューを次のようにロードできます。
1Route::get('/dashboard', function () {2 return view('courier::dashboard');3});
パッケージビューの上書き
loadViewsFromメソッドを使用すると、Laravelは実際にはビューの2つの場所を登録します。アプリケーションの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コマンドを実行すると、パッケージのビューは指定された公開場所にコピーされます。
Viewコンポーネント
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名前空間内に存在するCalendarおよびColorPickerコンポーネントがあるかもしれません。
1use Illuminate\Support\Facades\Blade;2 3/**4 * Bootstrap your package's services.5 */6public function boot(): void7{8 Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');9}
これにより、package-name::構文を使用して、ベンダー名前空間でパッケージコンポーネントを使用できるようになります。
1<x-nightshade::calendar />2<x-nightshade::color-picker />
Bladeは、コンポーネント名をパスカルケースにすることで、このコンポーネントにリンクされているクラスを自動的に検出します。サブディレクトリも「ドット」記法でサポートされています。
匿名コンポーネント
パッケージに匿名コンポーネントが含まれている場合、それらはパッケージの「ビュー」ディレクトリ(loadViewsFromメソッドで指定)のcomponentsディレクトリ内に配置する必要があります。その後、コンポーネント名の前にパッケージのビュー名前空間を付けてレンダリングできます。
1<x-courier::alert />
"About" Artisanコマンド
Laravelに組み込まれているabout Artisanコマンドは、アプリケーションの環境と設定の概要を提供します。パッケージは、AboutCommandクラスを介してこのコマンドの出力に追加情報をプッシュできます。通常、この情報はパッケージサービスプロバイダのbootメソッドから追加できます。
1use Illuminate\Foundation\Console\AboutCommand;2 3/**4 * Bootstrap any application services.5 */6public function boot(): void7{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のoptimizeコマンドは、アプリケーションの設定、イベント、ルート、ビューをキャッシュします。optimizesメソッドを使用して、optimizeおよびoptimize: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}
公開アセット
パッケージには、JavaScript、CSS、画像などのアセットが含まれる場合があります。これらのアセットをアプリケーションのpublicディレクトリに公開するには、サービスプロバイダのpublishesメソッドを使用します。この例では、関連するアセットのグループを簡単に公開するために使用できるpublicアセットグループタグも追加します。
1/**2 * Bootstrap any package services.3 */4public function boot(): void5{6 $this->publishes([7 __DIR__.'/../public' => public_path('vendor/courier'),8 ], 'public');9}
これで、パッケージのユーザーがvendor:publishコマンドを実行すると、アセットは指定された公開場所にコピーされます。ユーザーは通常、パッケージが更新されるたびにアセットを上書きする必要があるため、--forceフラグを使用できます。
1php artisan vendor:publish --tag=public --force
ファイルグループの公開
パッケージのアセットとリソースのグループを別々に公開したい場合があります。たとえば、パッケージのアセットを公開することを強制されずに、パッケージの設定ファイルを公開できるようにしたいかもしれません。これを行うには、パッケージのサービスプロバイダからpublishesメソッドを呼び出す際に、それらを「タグ付け」します。たとえば、courierパッケージのサービスプロバイダのbootメソッドで、タグを使用して2つの公開グループ(courier-configとcourier-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