包开发
介绍
包是向 Laravel 添加功能的主要方式。包可以是任何东西,可以是像 Carbon 这样处理日期的好方法,也可以是像 Spatie 的 Laravel Media Library 这样将文件与 Eloquent 模型关联的包。
有不同类型的包。某些包是独立的,意味着它们适用于任何 PHP 框架。Carbon 和 PHPUnit 就是独立包的示例。可以通过在 composer.json
文件中引用它们来将这些包与 Laravel 一起使用。
另一方面,其他一些包专门用于与 Laravel 一起使用。这些包可能具有路由、控制器、视图和配置,专门用于增强 Laravel 应用程序。本指南主要涵盖那些 Laravel 特定的包的开发。
关于门面
在编写 Laravel 应用程序时,通常使用契约或门面都无关紧要,因为两者都提供了基本相同的可测试性水平。但是,在编写包时,您的包通常无法访问 Laravel 的所有测试帮助程序。如果您希望能够像在典型的 Laravel 应用程序中安装该包一样编写包测试,可以使用 Orchestral Testbench 包。
包的发现
在 Laravel 应用程序的 config/app.php
配置文件中,providers
选项定义了应由 Laravel 加载的服务提供程序列表。当某人安装了您的包时,通常希望将您的服务提供程序包含在此列表中。您可以在包的 composer.json
文件的附加部分中定义提供程序,而无需要求用户手动将其添加到列表中。除了服务提供程序,您还可以列出要注册的任何门面:
"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
方法:
/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->publishes([
__DIR__.'/../config/courier.php' => config_path('courier.php'),
]);
}
现在,当您的包的用户执行 Laravel 的 vendor:publish
命令时,文件将被复制到指定的发布位置。一旦您的配置已发布,可以像访问任何其他配置文件一样访问其值:
$value = config('courier.option');
您不应在配置文件中定义闭包。当用户执行
config:cache
Artisan 命令时,它们无法正确序列化。
默认包配置
您还可以将自己包的配置文件与应用程序的已发布副本合并。这将使您的用户只需在已发布的配置文件中定义他们想要覆盖的选项。要合并配置文件值,使用服务提供程序的 register
方法中的 mergeConfigFrom
方法。
mergeConfigFrom
方法接受您包的配置文件路径作为第一个参数,应用程序的配置文件副本的名称作为第二个参数:
/**
* Register any application services.
*/
public function register(): void
{
$this->mergeConfigFrom(
__DIR__.'/../config/courier.php', 'courier'
);
}
该方法只合并配置数组的第一级。如果用户部分定义多维配置数组,将不会合并丢失的选项。
路由
如果您的包包含路由,可以使用 loadRoutesFrom
方法加载它们。此方法将自动确定应用程序的路由是否已缓存,并且如果路由已缓存,将不会加载您的路由文件:
/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->loadRoutesFrom(__DIR__.'/../routes/web.php');
}
迁移
如果您的包包含数据库迁移,可以使用 loadMigrationsFrom
方法告知 Laravel 如何加载它们。loadMigrationsFrom
方法接受您的包迁移的路径作为其唯一参数:
/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
}
一旦您的包的迁移已注册,当执行 php artisan migrate
命令时,它们将自动运行。您不需要将它们导出到应用程序的 database/migrations
目录中。
语言文件
如果您的包包含语言文件,可以使用 loadTranslationsFrom
方法告知 Laravel 如何加载它们。例如,如果您的包名为 courier
,则应将以下内容添加到服务提供程序的 boot
方法中:
/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->loadTranslationsFrom(__DIR__.'/../lang', 'courier');
}
包的翻译行使用 package::file.line
语法约定进行引用。因此,您可以从 messages
文件中加载 courier
包的欢迎行,如下所示:
echo trans('courier::messages.welcome');
发布语言文件
如果要将您的包的语言文件发布到应用程序的 lang/vendor
目录中,可以使用服务提供程序的 publishes
方法。publishes
方法接受一组包路径及其所需的发布位置。例如,要发布 courier
包的语言文件,可以执行以下操作:
/**
* Bootstrap any package services.
*/
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
方法中:
/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');
}
使用完全合格的视图路径。因此,一旦在服务提供程序中注册了视图路径,就可以像这样从 courier
包中加载 dashboard
视图:
Route::get('/dashboard', function () {
return view('courier::dashboard');
});
覆盖包的视图
使用 loadViewsFrom
方法时,实际上 Laravel 为您的视图注册了两个位置:应用程序的 resources/views/vendor
目录和您在调用 loadViewsFrom
时指定的目录。因此,使用 courier
包作为示例,Laravel 首先检查开发人员是否将视图的自定义版本放置在 resources/views/vendor/courier
目录中。然后,如果视图未定制,Laravel 将搜索您在 loadViewsFrom
中指定的包视图目录。这使得包用户可以轻松自定义/覆盖包的视图。
发布视图
如果要将您包的视图文件发布到应用程序的 resources/views/vendor
目录中,可以使用服务提供程序的 publishes
方法。publishes
方法接受一组包视图路径及其所需的发布位置:
/**
* Bootstrap the package services.
*/
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 知道如何找到组件。通常,应将组件注册在包的服务提供程序的 boot
方法中:
use Illuminate\Support\Facades\Blade;
use VendorPackage\View\Components\AlertComponent;
/**
* Bootstrap your package's services.
*/
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;
/**
* Bootstrap your package's services.
*/
public function boot(): void
{
Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}
这样,就可以通过使用 package-name::
语法来使用包组件的供应商命名空间:
<x-nightshade::calendar />
<x-nightshade::color-picker />
Blade 将自动检测与该组件关联的类,通过 PascalCase
组件名称进行链接。使用 "." 符号也支持子目录。
匿名组件
如果您的包包含匿名组件,则必须将它们放置在包的 "views" 目录的 components
子目录中(由 loadViewsFrom
方法指定)。然后,可以通过在组件名称前缀处添加包的视图命名空间来渲染它们:
<x-courier::alert />
关于 Artisan 命令
Laravel 内置的 about
Artisan 命令提供了应用程序环境和配置的概要。包可以通过 AboutCommand
类向该命令的输出推送附加信息。通常,可以在包的服务提供程序的 boot
方法中添加此信息:
use Illuminate\Foundation\Console\AboutCommand;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
AboutCommand::add('My Package', fn () => ['Version' => '1.0.0']);
}
命令
要将您的包的 Artisan 命令注册到 Laravel 中,可以使用 commands
方法。此方法期望一个命令类名的数组。一旦命令被注册,您可以使用 Artisan CLI 来执行它们:
use Courier\Console\Commands\InstallCommand;
use Courier\Console\Commands\NetworkCommand;
/**
* Bootstrap any package services.
*/
public function boot(): void
{
if ($this->app->runningInConsole()) {
$this->commands([
InstallCommand::class,
NetworkCommand::class,
]);
}
}
公共资源
您的包可能包含 JavaScript、CSS 和图片等资源。要将这些资源发布到应用程序的公共目录中,可以使用服务提供者的 publishes
方法。在此示例中,我们还将添加一个公共资源组标签,用于方便地发布一组相关的资源:
/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->publishes([
__DIR__.'/../public' => public_path('vendor/courier'),
], 'public');
}
现在,当包的用户执行 vendor:publish
命令时,您的资源将被复制到指定的发布位置。由于用户通常需要在每次更新包时覆盖资源,您可以使用 --force
标志:
php artisan vendor:publish --tag=public --force
发布文件组
您可能想要单独发布包的资源和文件组。例如,您可能希望允许用户发布包的配置文件,而无需强制发布包的资源。通过在调用包的服务提供者的 publishes
方法时对它们进行"标记",可以实现这个目的。例如,让我们在包的服务提供者的 boot
方法中使用标签来定义 courier
包的两个发布组 (courier-config
和 courier-migrations
):
/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->publishes([
__DIR__.'/../config/package.php' => config_path('package.php')
], 'courier-config');
$this->publishes([
__DIR__.'/../database/migrations/' => database_path('migrations')
], 'courier-migrations');
}
现在,用户可以通过引用它们的标签来单独发布这些组,当执行 vendor:publish
命令时:
php artisan vendor:publish --tag=courier-config
No Comments