Packages 开发
介绍
包是向 Laravel 添加功能的主要方式。包可以是任何东西,例如像 Carbon 这样的日期处理工具,或者像 Spatie 的 Laravel Media Library 这样的允许你将文件与 Eloquent 模型关联的包。
包有不同类型。有些包是独立的,意味着它们可以与任何 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 就会自动加载它。除了服务提供者,您还可以列出任何希望注册的门面( facades ):
"extra": {
"laravel": {
"providers": [
"Barryvdh\\Debugbar\\ServiceProvider"
],
"aliases": {
"Debugbar": "Barryvdh\\Debugbar\\Facade"
}
}
},
一旦您的包配置了包发现,Laravel 在安装时会自动注册它的服务提供者和门面,为您的包的用户创建便捷的安装体验。
服务提供者
服务提供者 是您的包与 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');
您不应在配置文件中定义闭包。因为在用户执行 |
默认包配置
您还可以将自己的包配置文件与应用程序发布的副本合并。这将允许用户仅在发布的配置文件中定义他们实际想要覆盖的选项。要合并配置文件值,可以在服务提供者的 register
方法中使用 mergeConfigFrom
方法。
mergeConfigFrom
方法的第一个参数是您包的配置文件的路径,第二个参数是应用程序配置文件的名称:
/**
* 注册应用程序服务。
*/
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');
}
包的翻译行使用 包名::文件名.行名
语法约定进行引用。所以,您可以像这样加载 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');
}
包的视图使用 包名::视图名
语法约定进行引用。因此,一旦您的视图路径在服务提供者中注册,您就可以像这样从 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 将自动通过 Pascal 大小写规则检测与组件名称关联的类。子目录也可以使用 “点” 符号表示。
“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']);
}
这样,执行 php artisan about
命令时,您包的信息(如版本号)将出现在输出中。
命令
要将包的 Artisan 命令注册到 Laravel 中,可以使用 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,
]);
}
}
这样,在应用程序的控制台环境中,InstallCommand
和 NetworkCommand
就可以通过 Artisan 命令行工具执行了。
优化命令
Laravel 的 optimize
命令会缓存应用程序的配置、事件、路由和视图。通过使用 optimizes
方法,你可以注册包自己的 Artisan 命令,这些命令将在执行 optimize
和 optimize:clear
命令时被调用:
/**
* 启动任何包的服务。
*/
public function boot(): void
{
if ($this->app->runningInConsole()) {
$this->optimizes(
optimize: 'package:optimize',
clear: 'package:clear-optimizations',
);
}
}
这样,当执行 optimize
或 optimize:clear
命令时,你定义的命令 package:optimize
和 package:clear-optimizations
将会被调用。
公共资源
你的包可能包含一些资产,如 JavaScript、CSS 和图片。要将这些资产发布到应用程序的公共目录中,可以使用服务提供者的 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
方法时使用“标签”来实现这一点。例如,以下代码示范了如何在包的服务提供者的 boot
方法中使用标签定义两个发布组(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