软件包开发
简介
扩展包是向 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
1"extra": { 2 "laravel": { 3 "providers": [ 4 "Barryvdh\\Debugbar\\ServiceProvider" 5 ], 6 "aliases": { 7 "Debugbar": "Barryvdh\\Debugbar\\Facade" 8 } 9 }10},
一旦你的扩展包配置了自动发现,Laravel 将在安装时自动注册其服务提供器和 facades,为你的扩展包用户创建便捷的安装体验。
选择退出扩展包自动发现
如果你是一个扩展包的使用者,并且想为一个扩展包禁用扩展包自动发现,你可以在你的应用程序的 composer.json
文件的 extra
部分列出扩展包名称
1"extra": {2 "laravel": {3 "dont-discover": [4 "barryvdh/laravel-debugbar"5 ]6 }7},
你可以使用应用程序的 dont-discover
指令中的 *
字符禁用所有扩展包的扩展包自动发现
1"extra": {2 "laravel": {3 "dont-discover": [4 "*"5 ]6 }7},
服务提供器
服务提供器 是你的扩展包和 Laravel 之间的连接点。服务提供器负责将内容绑定到 Laravel 的 服务容器 中,并告知 Laravel 在哪里加载扩展包资源,例如视图、配置和语言文件。
服务提供器扩展了 Illuminate\Support\ServiceProvider
类,并包含两个方法:register
和 boot
。基础 ServiceProvider
类位于 illuminate/support
Composer 扩展包中,你应该将其添加到你自己的扩展包的依赖项中。要了解更多关于服务提供器的结构和用途,请查看 它们的文档。
资源
配置
通常,你需要将你的扩展包的配置文件发布到应用程序的 config
目录。这将允许你的扩展包的用户轻松覆盖你的默认配置选项。要允许发布你的配置文件,请从你的服务提供器的 boot
方法中调用 publishes
方法
1/**2 * Bootstrap any package services.3 */4public function boot(): void5{6 $this->publishes([7 __DIR__.'/../config/courier.php' => config_path('courier.php'),8 ]);9}
现在,当你的扩展包的用户执行 Laravel 的 vendor:publish
命令时,你的文件将被复制到指定的发布位置。一旦你的配置被发布,它的值可以像任何其他配置文件一样被访问
1$value = config('courier.option');
你不应该在你的配置文件中定义闭包。当用户执行 config:cache
Artisan 命令时,它们无法被正确序列化。
默认扩展包配置
你也可以将你自己的扩展包配置文件与应用程序的已发布副本合并。这将允许你的用户只定义他们实际想要在配置文件的已发布副本中覆盖的选项。要合并配置文件值,请在你的服务提供器的 register
方法中使用 mergeConfigFrom
方法。
mergeConfigFrom
方法接受你的扩展包配置文件的路径作为它的第一个参数,以及应用程序的配置文件副本的名称作为它的第二个参数
1/**2 * Register any application services.3 */4public function register(): void5{6 $this->mergeConfigFrom(7 __DIR__.'/../config/courier.php', 'courier'8 );9}
此方法仅合并配置数组的第一层。如果你的用户部分定义了一个多维配置数组,则缺少的选项将不会被合并。
路由
如果你的扩展包包含路由,你可以使用 loadRoutesFrom
方法加载它们。此方法将自动确定应用程序的路由是否被缓存,如果路由已经被缓存,则不会加载你的路由文件
1/**2 * Bootstrap any package services.3 */4public function boot(): void5{6 $this->loadRoutesFrom(__DIR__.'/../routes/web.php');7}
迁移
如果你的扩展包包含 数据库迁移,你可以使用 publishesMigrations
方法告知 Laravel 给定的目录或文件包含迁移。当 Laravel 发布迁移时,它将自动更新它们文件名中的时间戳以反映当前日期和时间
1/**2 * Bootstrap any package services.3 */4public function boot(): void5{6 $this->publishesMigrations([7 __DIR__.'/../database/migrations' => database_path('migrations'),8 ]);9}
语言文件
如果你的扩展包包含 语言文件,你可以使用 loadTranslationsFrom
方法告知 Laravel 如何加载它们。例如,如果你的扩展包名为 courier
,你应该将以下内容添加到你的服务提供器的 boot
方法中
1/**2 * Bootstrap any package services.3 */4public function boot(): void5{6 $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier');7}
扩展包翻译行使用 package::file.line
语法约定引用。因此,你可以像这样从 messages
文件中加载 courier
扩展包的 welcome
行
1echo trans('courier::messages.welcome');
你可以使用 loadJsonTranslationsFrom
方法为你的扩展包注册 JSON 翻译文件。此方法接受包含你的扩展包的 JSON 翻译文件的目录路径
1/**2 * Bootstrap any package services.3 */4public function boot(): void5{6 $this->loadJsonTranslationsFrom(__DIR__.'/../lang');7}
发布语言文件
如果你想将你的扩展包的语言文件发布到应用程序的 lang/vendor
目录,你可以使用服务提供器的 publishes
方法。publishes
方法接受一个扩展包路径数组及其期望的发布位置。例如,要发布 courier
扩展包的语言文件,你可以这样做
1/** 2 * Bootstrap any package services. 3 */ 4public function boot(): void 5{ 6 $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier'); 7 8 $this->publishes([ 9 __DIR__.'/../lang' => $this->app->langPath('vendor/courier'),10 ]);11}
现在,当你的扩展包的用户执行 Laravel 的 vendor:publish
Artisan 命令时,你的扩展包的语言文件将被发布到指定的发布位置。
视图
要向 Laravel 注册你的扩展包的 视图,你需要告诉 Laravel 视图位于哪里。你可以使用服务提供器的 loadViewsFrom
方法来做到这一点。loadViewsFrom
方法接受两个参数:你的视图模板的路径和你的扩展包的名称。例如,如果你的扩展包的名称是 courier
,你将把以下内容添加到你的服务提供器的 boot
方法中
1/**2 * Bootstrap any package services.3 */4public function boot(): void5{6 $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');7}
扩展包视图使用 package::view
语法约定引用。因此,一旦你的视图路径在服务提供器中注册,你可以像这样从 courier
扩展包加载 dashboard
视图
1Route::get('/dashboard', function () {2 return view('courier::dashboard');3});
覆盖扩展包视图
当你使用 loadViewsFrom
方法时,Laravel 实际上为你的视图注册了两个位置:应用程序的 resources/views/vendor
目录和你指定的目录。因此,以 courier
扩展包为例,Laravel 将首先检查开发者是否已将视图的自定义版本放置在 resources/views/vendor/courier
目录中。然后,如果视图没有被自定义,Laravel 将搜索你在调用 loadViewsFrom
时指定的扩展包视图目录。这使得扩展包用户可以轻松地自定义/覆盖你的扩展包的视图。
发布视图
如果你想让你的视图可以发布到应用程序的 resources/views/vendor
目录,你可以使用服务提供器的 publishes
方法。publishes
方法接受一个扩展包视图路径数组及其期望的发布位置
1/** 2 * Bootstrap the package services. 3 */ 4public function boot(): void 5{ 6 $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier'); 7 8 $this->publishes([ 9 __DIR__.'/../resources/views' => resource_path('views/vendor/courier'),10 ]);11}
现在,当你的扩展包的用户执行 Laravel 的 vendor:publish
Artisan 命令时,你的扩展包的视图将被复制到指定的发布位置。
视图组件
如果你正在构建一个使用 Blade 组件或将组件放置在非常规目录中的扩展包,你将需要手动注册你的组件类及其 HTML 标签别名,以便 Laravel 知道在哪里找到该组件。你通常应该在你的扩展包的服务提供器的 boot
方法中注册你的组件
1use Illuminate\Support\Facades\Blade; 2use VendorPackage\View\Components\AlertComponent; 3 4/** 5 * Bootstrap your package's services. 6 */ 7public function boot(): void 8{ 9 Blade::component('package-alert', AlertComponent::class);10}
一旦你的组件被注册,它就可以使用它的标签别名来渲染
1<x-package-alert/>
自动加载扩展包组件
或者,你可以使用 componentNamespace
方法按照约定自动加载组件类。例如,Nightshade 扩展包可能具有 Calendar 和 ColorPicker 组件,它们位于 Nightshade\Views\Components
命名空间中
1use Illuminate\Support\Facades\Blade;2 3/**4 * Bootstrap your package's services.5 */6public function boot(): void7{8 Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');9}
这将允许使用 package-name::
语法通过它们的供应商命名空间来使用扩展包组件
1<x-nightshade::calendar />2<x-nightshade::color-picker />
Blade 将通过 pascal-casing 组件名称自动检测链接到此组件的类。也支持使用“点”表示法的子目录。
匿名组件
如果你的扩展包包含匿名组件,它们必须放置在你的扩展包的“views”目录的 components
目录中(如 loadViewsFrom
方法 所指定)。然后,你可以通过在组件名称前加上扩展包的视图命名空间来渲染它们
1<x-courier::alert />
"About" Artisan 命令
Laravel 内置的 about
Artisan 命令提供了应用程序环境和配置的概要。扩展包可以通过 AboutCommand
类将附加信息推送到此命令的输出。通常,此信息可以从你的扩展包服务提供器的 boot
方法中添加
1use Illuminate\Foundation\Console\AboutCommand;2 3/**4 * Bootstrap any application services.5 */6public function boot(): void7{8 AboutCommand::add('My Package', fn () => ['Version' => '1.0.0']);9}
命令
要向 Laravel 注册你的扩展包的 Artisan 命令,你可以使用 commands
方法。此方法需要一个命令类名称数组。一旦命令被注册,你可以使用 Artisan CLI 执行它们
1use Courier\Console\Commands\InstallCommand; 2use Courier\Console\Commands\NetworkCommand; 3 4/** 5 * Bootstrap any package services. 6 */ 7public function boot(): void 8{ 9 if ($this->app->runningInConsole()) {10 $this->commands([11 InstallCommand::class,12 NetworkCommand::class,13 ]);14 }15}
优化命令
Laravel 的 optimize
命令 缓存应用程序的配置、事件、路由和视图。使用 optimizes
方法,你可以注册你自己的扩展包的 Artisan 命令,这些命令应该在执行 optimize
和 optimize:clear
命令时被调用
1/** 2 * Bootstrap any package services. 3 */ 4public function boot(): void 5{ 6 if ($this->app->runningInConsole()) { 7 $this->optimizes( 8 optimize: 'package:optimize', 9 clear: 'package:clear-optimizations',10 );11 }12}
公共资源
你的扩展包可能具有诸如 JavaScript、CSS 和图像之类的资源。要将这些资源发布到应用程序的 public
目录,请使用服务提供器的 publishes
方法。在此示例中,我们还将添加一个公共资源组标签,该标签可用于轻松发布相关资源组
1/**2 * Bootstrap any package services.3 */4public function boot(): void5{6 $this->publishes([7 __DIR__.'/../public' => public_path('vendor/courier'),8 ], 'public');9}
现在,当你的扩展包的用户执行 vendor:publish
命令时,你的资源将被复制到指定的发布位置。由于用户通常需要在每次扩展包更新时覆盖资源,你可以使用 --force
标志
1php artisan vendor:publish --tag=public --force
发布文件组
你可能希望分别发布扩展包的资源组和资源。例如,你可能想允许你的用户发布你的扩展包的配置文件,而无需强制发布你的扩展包的资源。你可以通过在从扩展包的服务提供器调用 publishes
方法时“标记”它们来做到这一点。例如,让我们在扩展包的服务提供器的 boot
方法中使用标签为 courier
扩展包定义两个发布组(courier-config
和 courier-migrations
)
1/** 2 * Bootstrap any package services. 3 */ 4public function boot(): void 5{ 6 $this->publishes([ 7 __DIR__.'/../config/package.php' => config_path('package.php') 8 ], 'courier-config'); 9 10 $this->publishesMigrations([11 __DIR__.'/../database/migrations/' => database_path('migrations')12 ], 'courier-migrations');13}
现在,你的用户可以通过在执行 vendor:publish
命令时引用它们的标签来分别发布这些组
1php artisan vendor:publish --tag=courier-config