包开发
介绍
包是向 Laravel 添加功能的主要方式。包可以是任何东西,从一个处理日期的好方法如 Carbon,到一个完整的 BDD 测试框架如 Behat。
包有不同的类型。有些包是独立的,意味着它们可以与任何 PHP 框架一起工作。Carbon 和 Behat 就是独立包的例子。任何这些包都可以通过在你的 composer.json
文件中请求它们来与 Laravel 一起使用。
另一方面,其他包是专门为 Laravel 使用而设计的。这些包可能有路由、控制器、视图和配置,专门用于增强 Laravel 应用程序。本指南主要涵盖那些专门为 Laravel 开发的包。
关于 Facades 的说明
在编写 Laravel 应用程序时,使用 contracts 或 facades 通常无关紧要,因为两者都提供了基本相同的可测试性。然而,在编写包时,你的包通常无法访问所有 Laravel 的测试助手。如果你希望能够像在典型的 Laravel 应用程序中那样编写包测试,可以使用 Orchestral Testbench 包。
包发现
在 Laravel 应用程序的 config/app.php
配置文件中,providers
选项定义了 Laravel 应该加载的服务提供者列表。当有人安装你的包时,你通常希望你的服务提供者被包含在这个列表中。你可以在包的 composer.json
文件的 extra
部分中定义提供者,而不是要求用户手动将你的服务提供者添加到列表中。除了服务提供者,你还可以列出任何你希望注册的 facades:
"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
方法:
/**
* 启动任何应用程序服务。
*
* @return void
*/
public function boot()
{
$this->publishes([
__DIR__.'/path/to/config/courier.php' => config_path('courier.php'),
]);
}
现在,当你的包用户执行 Laravel 的 vendor:publish
命令时,你的文件将被复制到指定的发布位置。一旦你的配置被发布,其值可以像任何其他配置文件一样访问:
$value = config('courier.option');
你不应该在配置文件中定义闭包。当用户执行 config:cache
Artisan 命令时,它们无法正确序列化。
默认包配置
你还可以将自己的包配置文件与应用程序的已发布副本合并。这将允许你的用户仅在已发布的配置副本中定义他们实际上想要覆盖的选项。要合并配置,请在服务提供者的 register
方法中使用 mergeConfigFrom
方法:
/**
* 注册任何应用程序服务。
*
* @return void
*/
public function register()
{
$this->mergeConfigFrom(
__DIR__.'/path/to/config/courier.php', 'courier'
);
}
此方法仅合并配置数组的第一级。如果你的用户部分定义了多维配置数组,缺少的选项将不会被合并。
路由
如果你的包包含路由,可以使用 loadRoutesFrom
方法加载它们。此方法将自动确定应用程序的路由是否已缓存,并且如果路由已缓存,将不会加载你的路由文件:
/**
* 启动任何应用程序服务。
*
* @return void
*/
public function boot()
{
$this->loadRoutesFrom(__DIR__.'/routes.php');
}
迁移
如果你的包包含 数据库迁移,可以使用 loadMigrationsFrom
方法告知 Laravel 如何加载它们。loadMigrationsFrom
方法接受包的迁移路径作为其唯一参数:
/**
* 启动任何应用程序服务。
*
* @return void
*/
public function boot()
{
$this->loadMigrationsFrom(__DIR__.'/path/to/migrations');
}
一旦你的包的迁移被注册,当执行 php artisan migrate
命令时,它们将自动运行。你不需要将它们导出到应用程序的主 database/migrations
目录。
工厂
如果你的包包含 数据库工厂,可以使用 loadFactoriesFrom
方法告知 Laravel 如何加载它们。loadFactoriesFrom
方法接受包的工厂路径作为其唯一参数:
/**
* 启动任何应用程序服务。
*
* @return void
*/
public function boot()
{
$this->loadFactoriesFrom(__DIR__.'/path/to/factories');
}
一旦你的包的工厂被注册,你可以在应用程序中使用它们:
factory(Package\Namespace\Model::class)->create();
翻译
如果你的包包含 翻译文件,可以使用 loadTranslationsFrom
方法告知 Laravel 如何加载它们。例如,如果你的包名为 courier
,你应该在服务提供者的 boot
方法中添加以下内容:
/**
* 启动任何应用程序服务。
*
* @return void
*/
public function boot()
{
$this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'courier');
}
包翻译使用 package::file.line
语法约定引用。因此,你可以像这样从 messages
文件中加载 courier
包的 welcome
行:
echo trans('courier::messages.welcome');
发布翻译
如果你想将包的翻译发布到应用程序的 resources/lang/vendor
目录,可以使用服务提供者的 publishes
方法。publishes
方法接受包路径及其所需发布位置的数组。例如,要发布 courier
包的翻译文件,可以执行以下操作:
/**
* 启动任何应用程序服务。
*
* @return void
*/
public function boot()
{
$this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'courier');
$this->publishes([
__DIR__.'/path/to/translations' => resource_path('lang/vendor/courier'),
]);
}
现在,当你的包用户执行 Laravel 的 vendor:publish
Artisan 命令时,你的包的翻译将被发布到指定的发布位置。
视图
要将包的 视图 注册到 Laravel,你需要告诉 Laravel 视图的位置。可以使用服务提供者的 loadViewsFrom
方法来实现。loadViewsFrom
方法接受两个参数:视图模板的路径和包的名称。例如,如果包的名称是 courier
,你可以在服务提供者的 boot
方法中添加以下内容:
/**
* 启动任何应用程序服务。
*
* @return void
*/
public function boot()
{
$this->loadViewsFrom(__DIR__.'/path/to/views', 'courier');
}
包视图使用 package::view
语法约定引用。因此,一旦视图路径在服务提供者中注册,你可以像这样从 courier
包中加载 admin
视图:
Route::get('admin', function () {
return view('courier::admin');
});
覆盖包视图
当你使用 loadViewsFrom
方法时,Laravel 实际上为你的视图注册了两个位置:应用程序的 resources/views/vendor
目录和你指定的目录。因此,使用 courier
示例,Laravel 将首先检查开发者是否在 resources/views/vendor/courier
中提供了视图的自定义版本。然后,如果视图没有被自定义,Laravel 将搜索你在调用 loadViewsFrom
时指定的包视图目录。这使得包用户可以轻松自定义/覆盖包的视图。
发布视图
如果你想让视图可以发布到应用程序的 resources/views/vendor
目录,可以使用服务提供者的 publishes
方法。publishes
方法接受包视图路径及其所需发布位置的数组:
/**
* 启动任何应用程序服务。
*
* @return void
*/
public function boot()
{
$this->loadViewsFrom(__DIR__.'/path/to/views', 'courier');
$this->publishes([
__DIR__.'/path/to/views' => resource_path('views/vendor/courier'),
]);
}
现在,当你的包用户执行 Laravel 的 vendor:publish
Artisan 命令时,你的包的视图将被复制到指定的发布位置。
视图组件
如果你的包包含 视图组件,可以使用 loadViewComponentsAs
方法告知 Laravel 如何加载它们。loadViewComponentsAs
方法接受两个参数:视图组件的标签前缀和视图组件类的数组。例如,如果包的前缀是 courier
,并且你有 Alert
和 Button
视图组件,你可以在服务提供者的 boot
方法中添加以下内容:
/**
* 启动任何应用程序服务。
*
* @return void
*/
public function boot()
{
$this->loadViewComponentsAs('courier', [
Alert::class,
Button::class,
]);
}
一旦视图组件在服务提供者中注册,你可以在视图中像这样引用它们:
<x-courier-alert />
<x-courier-button />
匿名组件
如果你的包包含匿名组件,它们必须放置在包的“视图”目录(由 loadViewsFrom
指定)的 components
目录中。然后,你可以通过在组件名称前加上包的视图命名空间来渲染它们:
<x-courier::alert />
命令
要将包的 Artisan 命令注册到 Laravel,可以使用 commands
方法。此方法期望一个命令类名的数组。一旦命令被注册,你可以使用 Artisan CLI 执行它们:
/**
* 启动应用程序服务。
*
* @return void
*/
public function boot()
{
if ($this->app->runningInConsole()) {
$this->commands([
FooCommand::class,
BarCommand::class,
]);
}
}
公共资源
你的包可能有 JavaScript、CSS 和图像等资源。要将这些资源发布到应用程序的 public
目录,可以使用服务提供者的 publishes
方法。在此示例中,我们还将添加一个 public
资源组标签,可以用来发布相关资源组:
/**
* 启动任何应用程序服务。
*
* @return void
*/
public function boot()
{
$this->publishes([
__DIR__.'/path/to/assets' => public_path('vendor/courier'),
], 'public');
}
现在,当你的包用户执行 vendor:publish
命令时,你的资源将被复制到指定的发布位置。由于通常需要在每次包更新时覆盖资源,可以使用 --force
标志:
php artisan vendor:publish --tag=public --force
发布文件组
你可能希望单独发布包的资源和资源组。例如,你可能希望允许用户发布包的配置文件,而不必强制发布包的资源。可以通过在包的服务提供者的 publishes
方法中“标记”它们来实现。例如,让我们在包服务提供者的 boot
方法中使用标签定义两个发布组:
/**
* 启动任何应用程序服务。
*
* @return void
*/
public function boot()
{
$this->publishes([
__DIR__.'/../config/package.php' => config_path('package.php')
], 'config');
$this->publishes([
__DIR__.'/../database/migrations/' => database_path('migrations')
], 'migrations');
}
现在你的用户可以通过在执行 vendor:publish
命令时引用它们的标签来单独发布这些组:
php artisan vendor:publish --tag=config