包开发
简介
包是为 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 自动加载它。除了服务提供者之外,你还可以列出任何你想要注册的门面:
"extra": {
"laravel": {
"providers": [
"Barryvdh\\Debugbar\\ServiceProvider"
],
"aliases": {
"Debugbar": "Barryvdh\\Debugbar\\Facade"
}
}
},一旦你的包配置为可发现,Laravel 将在安装时自动注册其服务提供者和门面,为包的用户创造便捷的安装体验。
选择退出包发现
如果你是包的消费者,并且希望禁用某个包的发现功能,你可以在应用程序的 composer.json 文件的 extra 部分中列出该包的名称:
"extra": {
"laravel": {
"dont-discover": [
"barryvdh/laravel-debugbar"
]
}
},你可以在应用程序的 dont-discover 指令中使用 * 字符来禁用所有包的发现:
"extra": {
"laravel": {
"dont-discover": [
"*"
]
}
},服务提供者
服务提供者是你的包与 Laravel 之间的连接点。服务提供者负责将内容绑定到 Laravel 的服务容器中,并告知 Laravel 在哪里加载包资源,例如视图、配置和语言文件。
服务提供者扩展了 Illuminate\Support\ServiceProvider 类,并包含两个方法:register 和 boot。基础的 ServiceProvider 类位于 illuminate/support Composer 包中,你应该将其添加到你的包的依赖项中。要了解更多关于服务提供者的结构和用途,请查看它们的文档。
资源
配置
通常,你需要将包的配置文件发布到应用程序的 config 目录。这将允许你的包的用户轻松覆盖你的默认配置选项。要允许配置文件被发布,请在服务提供者的 boot 方法中调用 publishes 方法:
/**
* 引导任何包服务。
*/
public function boot(): void
{
$this->publishes([
__DIR__.'/../config/courier.php' => config_path('courier.php'),
]);
}现在,当你的包的用户执行 Laravel 的 vendor:publish 命令时,你的文件将被复制到指定的发布位置。一旦你的配置被发布,就可以像任何其他配置文件一样访问其值:
$value = config('courier.option');WARNING
你不应在配置文件中定义闭包。当用户执行 config:cache Artisan 命令时,它们无法被正确序列化。
默认包配置
你也可以将你自己的包配置文件与应用程序已发布的副本合并。这将允许你的用户仅在配置文件的已发布副本中定义他们真正想要覆盖的选项。要合并配置文件的值,请在你的服务提供者的 register 方法中使用 mergeConfigFrom 方法。
mergeConfigFrom 方法接受包的配置文件的路径作为第一个参数,应用程序配置文件的副本名称作为第二个参数:
/**
* 注册任何包服务。
*/
public function register(): void
{
$this->mergeConfigFrom(
__DIR__.'/../config/courier.php', 'courier'
);
}WARNING
此方法仅合并配置数组的第一级。如果你的用户部分定义了一个多维配置数组,则缺失的选项将不会被合并。
路由
如果你的包包含路由,你可以使用 loadRoutesFrom 方法加载它们。此方法将自动确定应用程序的路由是否已缓存,如果路由已被缓存,则不会加载你的路由文件:
/**
* 引导任何包服务。
*/
public function boot(): void
{
$this->loadRoutesFrom(__DIR__.'/../routes/web.php');
}迁移
如果你的包包含数据库迁移,你可以使用 publishesMigrations 方法来告知 Laravel 给定的目录或文件包含迁移。当 Laravel 发布迁移时,它会自动更新其文件名中的时间戳以反映当前日期和时间:
/**
* 引导任何包服务。
*/
public function boot(): void
{
$this->publishesMigrations([
__DIR__.'/../database/migrations' => database_path('migrations'),
]);
}语言文件
如果你的包包含语言文件,你可以使用 loadTranslationsFrom 方法来告知 Laravel 如何加载它们。例如,如果你的包名为 courier,你应该在服务提供者的 boot 方法中添加以下内容:
/**
* 引导任何包服务。
*/
public function boot(): void
{
$this->loadTranslationsFrom(__DIR__.'/../lang', 'courier');
}包翻译行使用 package::file.line 语法约定进行引用。因此,你可以像这样从 messages 文件加载 courier 包的 welcome 行:
echo trans('courier::messages.welcome');你可以使用 loadJsonTranslationsFrom 方法为你的包注册 JSON 翻译文件。此方法接受包含包 JSON 翻译文件的目录的路径:
/**
* 引导任何包服务。
*/
public function boot(): void
{
$this->loadJsonTranslationsFrom(__DIR__.'/../lang');
}发布语言文件
如果你希望将包的语言文件发布到应用程序的 lang/vendor 目录,你可以使用服务提供者的 publishes 方法。publishes 方法接受一个包路径及其所需发布位置的数组。例如,要发布 courier 包的语言文件,你可以执行以下操作:
/**
* 引导任何包服务。
*/
public function boot(): void
{
$this->loadTranslationsFrom(__DIR__.'/../lang', 'courier');
$this->publishes([
__DIR__.'/../lang' => $this->app->langPath('vendor/courier'),
]);
}现在,当你的包的用户执行 Laravel 的 vendor:publish Artisan 命令时,你的包的语言文件将被发布到指定的发布位置。
视图
要向 Laravel 注册你的包的视图,你需要告诉 Laravel 视图所在的位置。你可以使用服务提供者的 loadViewsFrom 方法来做到这一点。loadViewsFrom 方法接受两个参数:你的视图模板的路径和你的包的名称。例如,如果你的包名为 courier,你应该在服务提供者的 boot 方法中添加以下内容:
/**
* 引导任何包服务。
*/
public function boot(): void
{
$this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');
}包视图使用 package::view 语法约定进行引用。因此,一旦你的视图路径在服务提供者中注册,你就可以像这样从 courier 包加载 dashboard 视图:
Route::get('/dashboard', function () {
return view('courier::dashboard');
});覆盖包视图
当你使用 loadViewsFrom 方法时,Laravel 实际上为你的视图注册了两个位置:应用程序的 resources/views/vendor 目录和你指定的目录。因此,以 courier 包为例,Laravel 将首先检查开发者是否在 resources/views/vendor/courier 目录中放置了自定义版本的视图。然后,如果视图没有被自定义,Laravel 将搜索你在 loadViewsFrom 调用中指定的包视图目录。这使得包的用户可以轻松地自定义/覆盖你的包视图。
发布视图
如果你希望让你的视图可供发布到应用程序的 resources/views/vendor 目录,你可以使用服务提供者的 publishes 方法。publishes 方法接受一个包视图路径及其所需发布位置的数组:
/**
* 引导包服务。
*/
public function boot(): void
{
$this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');
$this->publishes([
__DIR__.'/../resources/views' => resource_path('views/vendor/courier'),
]);
}现在,当你的包的用户执行 Laravel 的 vendor:publish Artisan 命令时,你的包视图将被复制到指定的发布位置。
视图组件
如果你正在构建一个使用 Blade 组件的包,或者将组件放在非传统目录中,你将需要手动注册你的组件类及其 HTML 标签别名,以便 Laravel 知道在哪里找到该组件。通常,你应该在包的 service provider 的 boot 方法中注册你的组件:
use Illuminate\Support\Facades\Blade;
use VendorPackage\View\Components\AlertComponent;
/**
* 引导你的包的服务。
*/
public function boot(): void
{
Blade::component('package-alert', AlertComponent::class);
}一旦你的组件被注册,就可以使用它的标签别名进行渲染:
<x-package-alert/>自动加载包组件
或者,你可以使用 componentNamespace 方法按约定自动加载组件类。例如,一个 Nightshade 包可能有位于 Nightshade\Views\Components 命名空间中的 Calendar 和 ColorPicker 组件:
use Illuminate\Support\Facades\Blade;
/**
* 引导你的包的服务。
*/
public function boot(): void
{
Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}这将允许通过供应商命名空间使用 package-name:: 语法来使用包组件:
<x-nightshade::calendar />
<x-nightshade::color-picker />Blade 将通过将组件名称转换为帕斯卡命名法来自动检测链接到此组件的类。子目录也通过使用“点”符号得到支持。
匿名组件
如果你的包包含匿名组件,它们必须放在包的“视图”目录的 components 目录中(由 loadViewsFrom 方法指定)。然后,你可以通过用包的视图命名空间前缀组件名称来渲染它们:
<x-courier::alert />"About" Artisan 命令
Laravel 内置的 about Artisan 命令提供了应用程序环境和配置的概要。包可以通过 AboutCommand 类向此命令的输出推送附加信息。通常,此信息可以从你的包服务提供者的 boot 方法中添加:
use Illuminate\Foundation\Console\AboutCommand;
/**
* 引导任何包服务。
*/
public function boot(): void
{
AboutCommand::add('My Package', fn () => ['Version' => '1.0.0']);
}命令
要向 Laravel 注册你的包的 Artisan 命令,你可以使用 commands 方法。此方法需要一个命令类名的数组。一旦命令被注册,你就可以使用 Artisan CLI 执行它们:
use Courier\Console\Commands\InstallCommand;
use Courier\Console\Commands\NetworkCommand;
/**
* 引导任何包服务。
*/
public function boot(): void
{
if ($this->app->runningInConsole()) {
$this->commands([
InstallCommand::class,
NetworkCommand::class,
]);
}
}优化命令
Laravel 的优化命令会缓存应用程序的配置、事件、路由和视图。使用 optimizes 方法,你可以注册你自己的包 Artisan 命令,这些命令应在执行 optimize 和 optimize:clear 命令时被调用:
/**
* 引导任何包服务。
*/
public function boot(): void
{
if ($this->app->runningInConsole()) {
$this->optimizes(
optimize: 'package:optimize',
clear: 'package:clear-optimizations',
);
}
}重新加载命令
Laravel 的重新加载命令会终止任何正在运行的服务,以便它们可以被系统进程监视器自动重启。使用 reloads 方法,你可以注册你自己的包 Artisan 命令,这些命令应在执行 reload 命令时被调用:
/**
* 引导任何包服务。
*/
public function boot(): void
{
if ($this->app->runningInConsole()) {
$this->reloads('package:reload');
}
}公共资源
你的包可能拥有 JavaScript、CSS 和图像等资源。要将这些资源发布到应用程序的 public 目录,请使用服务提供者的 publishes 方法。在此示例中,我们还将添加一个 public 资源组标签,可用于轻松发布相关资源组:
/**
* 引导任何包服务。
*/
public function boot(): void
{
$this->publishes([
__DIR__.'/../public' => public_path('vendor/courier'),
], 'public');
}现在,当你的包的用户执行 vendor:publish 命令时,你的资源将被复制到指定的发布位置。由于用户通常需要在每次更新包时覆盖资源,他们可以使用 --force 标志:
php artisan vendor:publish --tag=public --force发布文件组
你可能希望分别发布包资产和资源组。例如,你可能希望允许用户发布包的配置文件,而不强制他们发布包的资产。你可以通过在包的 service provider 中调用 publishes 方法时使用“标签”来做到这一点。例如,让我们在包 service provider 的 boot 方法中使用标签为 courier 包定义两个发布组(courier-config 和 courier-migrations):
/**
* 引导任何包服务。
*/
public function boot(): void
{
$this->publishes([
__DIR__.'/../config/package.php' => config_path('package.php')
], 'courier-config');
$this->publishesMigrations([
__DIR__.'/../database/migrations/' => database_path('migrations')
], 'courier-migrations');
}现在,你的用户可以通过在执行 vendor:publish 命令时引用它们的标签来单独发布这些组:
php artisan vendor:publish --tag=courier-config你的用户也可以使用 --provider 标志发布你的包的 service provider 定义的所有可发布文件:
php artisan vendor:publish --provider="Your\Package\ServiceProvider"