扩展包开发
#扩展包开发
介绍
如果想要为在laravel中添加功能, 扩展包是最主要的方法, 扩展包可以实现像 Carbon 一样任何可以处理日期的功能, 也可以像 Spatie的Laravel Media Library一样允许我们将数据模型和文件相关联。
扩展包有不同的类型.有些扩展包自己就可以工作, 这意味着它可以工作在任何php扩展包下。Carbon 和phpUnit就是这种类型的扩展包的代表. 在laravel中如果想用这种类型的扩展包我们可以直接定义在 composer.json
文件里面.
除了上述类型的扩展包外, 另一种扩展包是专门针对laravel开发的. 这种类型类型的扩展包可能包含有路由,控制器, 视图,配置文件, 通过这些内容可以增强Laravel应用. 本文主要描述了这种类型的扩展包的开发规范。
关于 Facades 的说明
当开发一个Laravel应用的时候,由于契约或者门脸通常都提供了大体上相同的测试能力, 所以你可以任意选择使用契约或者门脸, 然而在开发扩展包的时候,你的扩展包通常可能无法访问所有的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
位于 Composer 扩展包的 illuminate/support
中,你必须将它添加到你的扩展包依赖项中。要了解有关服务提供者的结构和用途的更多信息,请查阅 它的文档.
资源文件
配置
通常,你需要将扩展包的配置文件发布到应用本身的 config
目录中。这样使用扩展包的用户就可以轻松的重写默认配置项。要发布配置文件,只需要在服务提供者的 boot
方法中调用 publishes
方法:
/**
* Bootstrap any package services.
*
* @return void
*/
public function boot()
{
$this->publishes([
__DIR__.'/../config/courier.php' => config_path('courier.php'),
]);
}
现在,当扩展包的用户执行 Laravel 的 vendor:publish
命令,扩展包文件将被复制到指定的目录中,发布配置后,就可以像其它配置一样被访问:
$value = config('courier.option');
注意:你不应该在配置文件中定义闭包函数。当用户执行
config:cache
Artisan 命令时,配置文件将不能被正确的序列化。
扩展包默认配置
你可以将扩展包默认配置和应用的已发布副本配置合并在一起。这样扩展包用户就可以在副本配置文件中定义他们想要覆盖的配置选项。想要合并配置,只需要在服务提供者的 register
方法中调用 mergeConfigFrom
方法即可:
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->mergeConfigFrom(
__DIR__.'/../config/courier.php', 'courier'
);
}
注意:此方法只合并配置数组的第一维。如果扩展包用户定义了多维配置数组,缺少的选项将不会被合并。
路由
如果你的扩展包中包含路由文件,你需要使用 loadRoutesFrom
方法加载它们。此方法将自动判断应用的路由是否已被缓存,如果路由已经被缓存,将不会加载你的路由文件:
/**
* Bootstrap any package services.
*
* @return void
*/
public function boot()
{
$this->loadRoutesFrom(__DIR__.'/../routes/web.php');
}
数据库迁移
如果你的扩展包中包含 数据库迁移,你需要使用 loadMigrationsFrom
方法告知 Laravel 如何加载它们。 loadMigrationsFrom
方法只需要扩展包迁移文件路径作为唯一参数:
/**
* Bootstrap any package services.
*
* @return void
*/
public function boot()
{
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
}
一旦你的扩展包迁移文件被注册,当运行 php artisan migrate
命令时它们就会被自动执行。你不需要将它们导入到应用的 database/migrations
目录中。
翻译
如果你的扩展包中包含 语言包文件 ,你需要使用 loadTranslationsFrom
方法告知 Laravel 如何加载它们。例如,如果你的扩展包名为 courier
,你需要将下面的内容加入到服务提供者的 boot
方法中:
/**
* Bootstrap any package services.
*
* @return void
*/
public function boot()
{
$this->loadTranslationsFrom(__DIR__.'/../resources/lang', 'courier');
}
扩展包翻译约定使用 package::file.line
语法进行引用。因此,你可以按照下面的方式来加载 courier
扩展包中的 messages
文件的 welcome
行:
echo trans('courier::messages.welcome');
发布语言包
如果你想要将扩展包中的语言包发布到应用的 resources/lang/vendor
目录中, 可以使用服务提供者的 publishes
方法。 publishes
方法接收一个包含语言包路径和对应发布位置的数组。例如,发布 courier
扩展包的语言包文件,操作如下:
/**
* Bootstrap any package services.
*
* @return void
*/
public function boot()
{
$this->loadTranslationsFrom(__DIR__.'/../resources/lang', 'courier');
$this->publishes([
__DIR__.'/../resources/lang' => resource_path('lang/vendor/courier'),
]);
}
现在,当扩展包的用户执行 Laravel 的 vendor:publish
Artisan 命令,语言包将会被发布到指定的目录中。
视图
想要在 Laravel 中注册你的扩展包的 视图 , 需要告知 Laravel 视图文件的位置。 你可以使用服务提供者的 loadViewsFrom
方法来实现。 loadViewsFrom
方法接收两个参数:视图模板的路径和扩展包名。例如,如果你的扩展包名为 courier
,你需要将下面的内容加入到服务提供者的 boot
方法中:
/**
* Bootstrap any package services.
*
* @return void
*/
public function boot()
{
$this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');
}
扩展包视图约定使用 package::view
语法进行引用。因此,一旦视图路径在服务提供者中注册成功,你可以通过下面的方式来加载 courier
扩展包中的 admin
视图:
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.
*
* @return void
*/
public function boot()
{
$this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');
$this->publishes([
__DIR__.'/../resources/views' => resource_path('views/vendor/courier'),
]);
}
现在,当扩展包的用户执行 Laravel 的 vendor:publish
Artisan 命令,扩展包视图将会被发布到指定的目录中。
视图组件
如果您的软件包包含视图组件,则可以使用 loadViewComponentsAs
方法通知 Laravel 如何加载它们。 loadViewComponentsAs
方法接受两个参数:视图组件的标签前缀和视图组件类组成的数组。 例如,如果包的前缀是 courier
,并且具有 Alert
和Button
视图组件,则可以将以下内容添加到服务提供者的 boot
方法中:
use Courier\Components\Alert;
use Courier\Components\Button;
/**
* Bootstrap any package services.
*
* @return void
*/
public function boot()
{
$this->loadViewComponentsAs('courier', [
Alert::class,
Button::class,
]);
}
一旦在服务提供者中注册了视图组件,就可以直接在视图中引用它们,如下所示:
<x-courier-alert />
<x-courier-button />
匿名组件
如果您的程序包包含匿名组件,则必须将它们放置在您程序包的 views
目录的 components
文件夹中(由 loadViewsFrom
指定)。 然后,可以通过在组件名称的前面加上包的视图命名空间来渲染它们:
<x-courier::alert />
命令
想要在 Laravel 中注册扩展包的 Artisan 命令,需要使用 commands
方法。此方法接收一个命令类的数组,一旦这些命令被注册成功,你可以使用 Artisan 命令行 执行他们:
use Courier\Console\Commands\InstallCommand;
use Courier\Console\Commands\NetworkCommand;
/**
* Bootstrap any package services.
*
* @return void
*/
public function boot()
{
if ($this->app->runningInConsole()) {
$this->commands([
InstallCommand::class,
NetworkCommand::class,
]);
}
}
公共资源文件
你的扩展包可能包含 JavaScript 、CSS 和图片之类的资源文件。要将这些资源发布到应用的 public
目录,可以使用服务提供者的 publishes
方法。在下面的例子中,我们也可以添加一个 public
资源组标签,该标签可用于发布相关资源组:
/**
* Bootstrap any package services.
*
* @return void
*/
public function boot()
{
$this->publishes([
__DIR__.'/../public' => public_path('vendor/courier'),
], 'public');
}
现在,当扩展包的用户执行 vendor:publish
命令,扩展包资源文件将会被发布到指定的目录中。 由于每次更新扩展包时通常都需要覆盖资源文件,因此需要使用--force
标签:
php artisan vendor:publish --tag=public --force
发布群组文件
你可能希望单独发布扩展包前端资源文件和模板视图文件。例如,你可能希望用户只发布扩展包的配置文件,而不是把扩展包的资源文件也一起发布了。你可以在扩展包中的服务提供者上调用 publishes
方法时对他们打上「标签」来实现。比如,让我们使用扩展包服务提供者中的 boot
方法来定义两个发布群组:(config
和 migrations
) 。
/**
* 启动任意应用服务.
*
* @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