软件包是为 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 方法:
/**
* 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');[!WARNING]
不应在配置文件中定义闭包。当用户执行config:cacheArtisan 命令时,它们无法正确序列化。
您也可以将您自己的包配置文件与应用程序已发布的副本进行合并。这将允许您的用户在配置文件的已发布副本中只定义他们实际想要覆盖的选项。要合并配置文件值,请在您的服务提供者的 register 方法中使用 mergeConfigFrom 方法。
mergeConfigFrom 方法接受您的包的配置文件路径作为第一个参数,以及应用程序中配置文件的副本名称作为第二个参数:
/**
* Register any package services.
*/
public function register(): void
{
$this->mergeConfigFrom(
__DIR__.'/../config/courier.php', 'courier'
);
}[!WARNING]
此方法只合并配置数组的第一层。如果您的用户部分定义了一个多维配置数组,则缺失的选项将不会被合并。
如果您的包包含路由,您可以使用 loadRoutesFrom 方法加载它们。此方法将自动确定应用程序的路由是否已缓存,并且如果路由已被缓存,则不会加载您的路由文件:
/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->loadRoutesFrom(__DIR__.'/../routes/web.php');
}如果您的包包含 数据库迁移,您可以使用 publishesMigrations 方法来告知 Laravel 给定的目录或文件包含迁移。当 Laravel 发布迁移时,它将自动更新其文件名中的时间戳以反映当前日期和时间:
/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->publishesMigrations([
__DIR__.'/../database/migrations' => database_path('migrations'),
]);
}如果你的包包含 语言文件,你可以使用 loadTranslationsFrom 方法来告知 Laravel 如何加载它们。例如,如果你的包名为 courier,你应该将以下代码添加到你的服务提供者的 boot 方法中:
/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->loadTranslationsFrom(__DIR__.'/../lang', 'courier');
}包翻译行使用 package::file.line 语法约定. 所以, 你可以加载 courier 包的 welcome 行从 messages 文件如下所示:
echo trans('courier::messages.welcome');你可以使用 loadJsonTranslationsFrom 方法为你的包注册 JSON 翻译文件。此方法接受包含你的包的 JSON 翻译文件的目录路径:
/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->loadJsonTranslationsFrom(__DIR__.'/../lang');
}如果你想要将你的包的语言文件发布到应用程序的 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');
}包视图使用 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 方法接受一个包含包视图路径及其所需发布位置的数组:
/**
* 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);
} Once your component has been registered, it may be rendered using its tag alias:
<x-package-alert/>或者,您可以使用 componentNamespace 方法,按照约定自动加载组件类。例如,一个 Nightshade 包可能包含 Calendar 和 ColorPicker 组件,它们位于 Nightshade\Views\Components 命名空间中:
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 将通过将组件名称转换为 Pascal 命名法,自动检测与该组件关联的类。子目录也支持使用“点”表示法。
如果您的包包含匿名组件,它们必须被放置在您的包的“views”目录 (如loadViewsFrom 方法所指定) 的一个 components 目录中。然后,您可以通过将组件名称与包的视图命名空间作为前缀来渲染它们:
<x-courier::alert />Laravel 内置的 about Artisan 命令提供了一个应用程序环境和配置的概要。包可以通过 AboutCommand 类向此命令的输出推送额外信息。通常,此信息可以从您的包服务提供者的 boot 方法中添加:
use Illuminate\Foundation\Console\AboutCommand;
/**
* Bootstrap any package services.
*/
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;
/**
* Bootstrap any package services.
*/
public function boot(): void
{
if ($this->app->runningInConsole()) {
$this->commands([
InstallCommand::class,
NetworkCommand::class,
]);
}
}Laravel 的 optimize 命令 缓存了应用程序的配置、事件、路由和视图。使用 optimizes 方法,你可以注册你自己的包的 Artisan 命令,当 optimize 和 optimize:clear 命令执行时应被调用:
/**
* Bootstrap any package services.
*/
public function boot(): void
{
if ($this->app->runningInConsole()) {
$this->optimizes(
optimize: 'package:optimize',
clear: 'package:clear-optimizations',
);
}
}你的软件包可能包含 JavaScript、CSS 和图片等资产。要将这些资产发布到应用程序的 public 目录中,请使用服务提供者的 publishes 方法。在此示例中,我们还将添加一个 public 资产组标签,它可用于轻松发布相关资产组:
/**
* 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->publishesMigrations([
__DIR__.'/../database/migrations/' => database_path('migrations')
], 'courier-migrations');
}现在您的用户可以通过在执行 vendor:publish 命令时引用它们的标签来分别发布这些分组:
php artisan vendor:publish --tag=courier-config你的用户还可以使用 --provider 标志发布由你的包的服务提供者定义的所有可发布文件:
php artisan vendor:publish --provider="Your\Package\ServiceProvider"