跳至内容

软件包开发

介绍

包是向 Laravel 添加功能的主要方式。包可以是任何内容,例如像Carbon这样处理日期的工具,也可以是像 Spatie 的Laravel 媒体库这样允许你将文件与 Eloquent 模型关联的包

软件包有多种类型。有些软件包是独立的,这意味着它们可以与任何 PHP 框架兼容。Carbon 和 Pest 就是独立软件包的例子。任何这些软件包都可以在 Laravel 中使用,只需在composer.json文件中引用它们即可。

另一方面,有些软件包是专门为 Laravel 设计的。这些软件包可能包含专门用于增强 Laravel 应用程序的路由、控制器、视图和配置。本指南主要介绍 Laravel 专用软件包的开发。

关于 Facade 的说明

编写 Laravel 应用程序时,使用契约或外观通常无关紧要,因为两者提供的可测试性基本相同。但是,编写扩展包时,您的扩展包通常无法访问 Laravel 的所有测试辅助函数。如果您希望能够像在典型的 Laravel 应用程序中安装扩展包一样编写扩展包测试,则可以使用Orchestral Testbench 扩展包。

包发现

Laravel 应用程序的bootstrap/providers.php文件包含 Laravel 应加载的服务提供商列表。但是,您无需要求用户手动将服务提供商添加到列表中,而是可以在extra包文件的 部分中定义提供商composer.json,以便 Laravel 自动加载它。除了服务提供商之外,您还可以列出您想要注册的任何外观:

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

一旦你的包配置好了发现,Laravel 将在安装时自动注册其服务提供商和外观,为你的包用户创造便捷的安装体验。

选择退出包发现

如果您是软件包的使用者,并且想要禁用软件包的软件包发现,您可以在extra应用程序composer.json文件的部分中列出软件包名称:

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/supportComposer 包中,您应该将其添加到您自己的包的依赖项中。要了解有关服务提供者的结构和用途的更多信息,请查看其文档

资源

配置

通常,您需要将软件包的配置文件发布到应用程序的config目录中。这将允许软件包的用户轻松覆盖默认配置选项。要允许发布配置文件,请publishesboot服务提供者的方法中调用该方法:

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');

你不应该在配置文件中定义闭包。当用户执行config:cacheArtisan 命令时,它们无法被正确序列化。

默认包配置

您还可以将您自己的软件包配置文件与应用程序的已发布副本合并。这将允许您的用户在已发布的配置文件副本中仅定义他们实际想要覆盖的选项。要合并配置文件值,请使用mergeConfigFrom服务提供商register方法中的相应方法。

mergeConfigFrom方法接受包的配置文件的路径作为其第一个参数,并接受应用程序的配置文件副本的名称作为其第二个参数:

1/**
2 * Register any application 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。因此,你可以像这样从文件中加载courier软件包的welcome行:messages

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

你可以使用该方法为你的包注册 JSON 翻译文件loadJsonTranslationsFrom。该方法接受包含包的 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:publishArtisan 命令时,您的包的语言文件将被发布到指定的发布位置。

视图

要将你的软件包视图注册到 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语法约定。因此,一旦视图路径在服务提供者中注册,就可以像这样从包dashboard中加载视图:courier

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:publishArtisan 命令时,您的包的视图将被复制到指定的发布位置。

视图组件

如果你正在构建一个使用 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包可能包含Calendar位于ColorPicker命名空间内的组件Nightshade\Views\Components

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 大小写格式对组件名称进行排序。子目录也支持使用“点”符号。

匿名组件

如果你的包中包含匿名组件,则必须将它们放置在包的“views”目录下(由loadViewsFrom 方法components指定)。然后,你可以在组件名称前添加包的视图命名空间来渲染它们:

1<x-courier::alert />

关于 Artisan Command

Laravel 内置的aboutArtisan 命令提供了应用程序环境和配置的概要。软件包可以通过类将附加信息推送到此命令的输出AboutCommand。通常,这些信息可以通过软件包服务提供商的boot方法添加:

1use Illuminate\Foundation\Console\AboutCommand;
2 
3/**
4 * Bootstrap any application 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方法,你可以注册包自己的 Artisan 命令,这些命令将在执行optimize和命令时调用:optimize:clear

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(): 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从软件包的服务提供商调用方法时为它们添加“标签”来实现这一点。例如,让我们在软件包的服务提供商的方法中使用标签为软件包定义两个发布组couriercourier-configcourier-migrationsboot

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"