扩展包开发
Laravel 包开发#
介绍#
包是向 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 自动加载,而不用手动将其添加到服务提供者列表中。除了服务提供者外,你还可以列出你想注册的任何门面:
"extra": {
"laravel": {
"providers": [
"Barryvdh\\Debugbar\\ServiceProvider"
],
"aliases": {
"Debugbar": "Barryvdh\\Debugbar\\Facade"
}
}
},
当你的包配置了包发现后,Laravel 会在安装该包时自动注册服务提供者及 Facades,这样就为你的包用户创造一个便利的安装体验。
退出包发现#
如果你是包消费者,要禁用包发现功能,你可以在应用的 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
方法加载它们。这个方法会自动判断应用程序的路由是否被缓存,如果路由已经被缓存,则不会加载你的路由文件:
/**
* 引导包服务.
*/
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
语法约定引用。因此,一旦您的视图路径在服务提供者中注册,您就可以这样加载 courier
包的 messages
文件中的 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 知道在哪里找到组件。你通常应该在包的服务提供者的 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
包可能有 Calendar
和 ColorPicker
组件,它们位于 Nightshade\Views\Components
命名空间中:
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 将自动检测与此组件链接的类,通过转换组件名称的大写驼峰命名。子目录也支持使用「.」表示法。
匿名组件#
如果你的包包含匿名组件,必须将它们放在包的「视图」目录(如 loadViewsFrom
方法 所指定)的 components
目录中。然后,你可以通过在组件名称前加上包的视图命名空间来渲染它们:
<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,
]);
}
}
公共资源#
你的包可能有如 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
发布文件组#
你可能想要分别发布包资源和资源组。例如,你可能希望允许用户发布包的配置文件而不强迫发布包的资源。当从包的服务提供者调用 publishes
方法时,你可以通过「tagging」它们来实现这一点。例如,让我们在包服务提供者的 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
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
推荐文章: