Skip to content

包开发

简介

包是向 Laravel 添加功能的主要方式。包可以是任何东西,从处理日期的优秀工具 Carbon 到允许您将文件与 Eloquent 模型关联的包,如 Spatie 的 Laravel Media Library

包有不同类型。有些包是独立的,意味着它们可以与任何 PHP 框架一起使用。Carbon 和 Pest 是独立包的例子。任何这些包都可以通过在您的 composer.json 文件中要求它们来与 Laravel 一起使用。

另一方面,其他包是专门为 Laravel 使用而设计的。这些包可能具有路由、控制器、视图和配置,专门用于增强 Laravel 应用程序。本指南主要涵盖这些特定于 Laravel 的包的开发。

关于 Facades 的说明

在编写 Laravel 应用程序时,通常不管您使用合同还是 Facades,因为两者提供的测试能力基本相同。然而,在编写包时,您的包通常无法访问 Laravel 的所有测试助手。如果您希望能够像在典型的 Laravel 应用程序中安装包一样编写包测试,您可以使用 Orchestral Testbench 包。

包发现

Laravel 应用程序的 bootstrap/providers.php 文件包含应由 Laravel 加载的服务提供者列表。然而,您可以在包的 composer.json 文件的 extra 部分中定义提供者,以便 Laravel 自动加载它。除了服务提供者,您还可以列出任何希望注册的 facades

json
"extra": {
    "laravel": {
        "providers": [
            "Barryvdh\\Debugbar\\ServiceProvider"
        ],
        "aliases": {
            "Debugbar": "Barryvdh\\Debugbar\\Facade"
        }
    }
},

一旦您的包配置为发现,Laravel 将在安装时自动注册其服务提供者和 facades,为您的包用户创建一个方便的安装体验。

选择退出包发现

如果您是包的消费者并希望禁用某个包的包发现,您可以在应用程序的 composer.json 文件的 extra 部分列出包名:

json
"extra": {
    "laravel": {
        "dont-discover": [
            "barryvdh/laravel-debugbar"
        ]
    }
},

您可以使用 * 字符在应用程序的 dont-discover 指令中禁用所有包的包发现:

json
"extra": {
    "laravel": {
        "dont-discover": [
            "*"
        ]
    }
},

服务提供者

服务提供者 是您的包与 Laravel 之间的连接点。服务提供者负责将内容绑定到 Laravel 的 服务容器 中,并告知 Laravel 从何处加载包资源,如视图、配置和语言文件。

服务提供者扩展 Illuminate\Support\ServiceProvider 类,并包含两个方法:registerboot。基础 ServiceProvider 类位于 illuminate/support Composer 包中,您应将其添加到您自己包的依赖项中。要了解有关服务提供者的结构和目的的更多信息,请查看 它们的文档

资源

配置

通常,您需要将包的配置文件发布到应用程序的 config 目录。这将允许您的包用户轻松覆盖默认配置选项。要允许发布配置文件,请在服务提供者的 boot 方法中调用 publishes 方法:

php
/**
 * 启动任何包服务。
 */
public function boot(): void
{
    $this->publishes([
        __DIR__.'/../config/courier.php' => config_path('courier.php'),
    ]);
}

现在,当您的包用户执行 Laravel 的 vendor:publish 命令时,您的文件将被复制到指定的发布位置。一旦您的配置被发布,其值可以像任何其他配置文件一样访问:

php
$value = config('courier.option');

WARNING

您不应在配置文件中定义闭包。当用户执行 config:cache Artisan 命令时,它们无法正确序列化。

默认包配置

您还可以将自己的包配置文件与应用程序的已发布副本合并。这将允许您的用户仅在配置文件的已发布副本中定义他们实际想要覆盖的选项。要合并配置文件值,请在服务提供者的 register 方法中使用 mergeConfigFrom 方法。

mergeConfigFrom 方法接受包的配置文件路径作为第一个参数,应用程序的配置文件副本的名称作为第二个参数:

php
/**
 * 注册任何应用程序服务。
 */
public function register(): void
{
    $this->mergeConfigFrom(
        __DIR__.'/../config/courier.php', 'courier'
    );
}

WARNING

此方法仅合并配置数组的第一层。如果您的用户部分定义了多维配置数组,则缺失的选项将不会合并。

路由

如果您的包包含路由,您可以使用 loadRoutesFrom 方法加载它们。此方法将自动确定应用程序的路由是否已缓存,如果路由已缓存,则不会加载您的路由文件:

php
/**
 * 启动任何包服务。
 */
public function boot(): void
{
    $this->loadRoutesFrom(__DIR__.'/../routes/web.php');
}

迁移

如果您的包包含 数据库迁移,您可以使用 publishesMigrations 方法告知 Laravel 给定目录或文件包含迁移。当 Laravel 发布迁移时,它将自动更新其文件名中的时间戳,以反映当前的日期和时间:

php
/**
 * 启动任何包服务。
 */
public function boot(): void
{
    $this->publishesMigrations([
        __DIR__.'/../database/migrations' => database_path('migrations'),
    ]);
}

语言文件

如果您的包包含 语言文件,您可以使用 loadTranslationsFrom 方法告知 Laravel 如何加载它们。例如,如果您的包名为 courier,您应在服务提供者的 boot 方法中添加以下内容:

php
/**
 * 启动任何包服务。
 */
public function boot(): void
{
    $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier');
}

包翻译行使用 package::file.line 语法约定进行引用。因此,您可以像这样从 messages 文件中加载 courier 包的 welcome 行:

php
echo trans('courier::messages.welcome');

您可以使用 loadJsonTranslationsFrom 方法为您的包注册 JSON 翻译文件。此方法接受包含您包的 JSON 翻译文件的目录的路径:

php
/**
 * 启动任何包服务。
 */
public function boot(): void
{
    $this->loadJsonTranslationsFrom(__DIR__.'/../lang');
}

发布语言文件

如果您希望将包的语言文件发布到应用程序的 lang/vendor 目录,您可以使用服务提供者的 publishes 方法。publishes 方法接受一个包路径及其所需发布位置的数组。例如,要发布 courier 包的语言文件,您可以执行以下操作:

php
/**
 * 启动任何包服务。
 */
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 方法中添加以下内容:

php
/**
 * 启动任何包服务。
 */
public function boot(): void
{
    $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');
}

包视图使用 package::view 语法约定进行引用。因此,一旦您的视图路径在服务提供者中注册,您可以像这样从 courier 包加载 dashboard 视图:

php
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 方法接受一个包视图路径及其所需发布位置的数组:

php
/**
 * 启动包服务。
 */
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 方法中注册组件:

php
use Illuminate\Support\Facades\Blade;
use VendorPackage\View\Components\AlertComponent;

/**
 * 启动您的包服务。
 */
public function boot(): void
{
    Blade::component('package-alert', AlertComponent::class);
}

一旦您的组件被注册,您可以使用其标签别名进行渲染:

blade
<x-package-alert/>

自动加载包组件

或者,您可以使用 componentNamespace 方法按约定自动加载组件类。例如,一个 Nightshade 包可能有 CalendarColorPicker 组件,它们位于 Nightshade\Views\Components 命名空间中:

php
use Illuminate\Support\Facades\Blade;

/**
 * 启动您的包服务。
 */
public function boot(): void
{
    Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}

这将允许使用包组件通过其供应商命名空间使用 package-name:: 语法:

blade
<x-nightshade::calendar />
<x-nightshade::color-picker />

Blade 将通过 Pascal 大小写组件名称自动检测与此组件链接的类。子目录也支持使用“点”表示法。

匿名组件

如果您的包包含匿名组件,它们必须放置在包的“视图”目录的 components 目录中(如 loadViewsFrom 方法 所指定)。然后,您可以通过在组件名称前加上包的视图命名空间来渲染它们:

blade
<x-courier::alert />

"关于" Artisan 命令

Laravel 内置的 about Artisan 命令提供了应用程序环境和配置的概要。包可以通过 AboutCommand 类将额外信息推送到此命令的输出。通常,这些信息可以从您的包服务提供者的 boot 方法中添加:

php
use Illuminate\Foundation\Console\AboutCommand;

/**
 * 启动任何应用程序服务。
 */
public function boot(): void
{
    AboutCommand::add('My Package', fn () => ['Version' => '1.0.0']);
}

命令

要将包的 Artisan 命令注册到 Laravel,您可以使用 commands 方法。此方法期望一个命令类名称的数组。一旦命令被注册,您可以使用 Artisan CLI 执行它们:

php
use Courier\Console\Commands\InstallCommand;
use Courier\Console\Commands\NetworkCommand;

/**
 * 启动任何包服务。
 */
public function boot(): void
{
    if ($this->app->runningInConsole()) {
        $this->commands([
            InstallCommand::class,
            NetworkCommand::class,
        ]);
    }
}

优化命令

Laravel 的 optimize 命令 缓存应用程序的配置、事件、路由和视图。使用 optimizes 方法,您可以注册包自己的 Artisan 命令,这些命令应在执行 optimizeoptimize:clear 命令时被调用:

php
/**
 * 启动任何包服务。
 */
public function boot(): void
{
    if ($this->app->runningInConsole()) {
        $this->optimizes(
            optimize: 'package:optimize',
            clear: 'package:clear-optimizations',
        );
    }
}

公共资产

您的包可能有资产,如 JavaScript、CSS 和图像。要将这些资产发布到应用程序的 public 目录,请使用服务提供者的 publishes 方法。在此示例中,我们还将添加一个 public 资产组标签,可用于轻松发布相关资产组:

php
/**
 * 启动任何包服务。
 */
public function boot(): void
{
    $this->publishes([
        __DIR__.'/../public' => public_path('vendor/courier'),
    ], 'public');
}

现在,当您包的用户执行 vendor:publish 命令时,您的资产将被复制到指定的发布位置。由于用户通常需要在每次更新包时覆盖资产,您可以使用 --force 标志:

shell
php artisan vendor:publish --tag=public --force

发布文件组

您可能希望单独发布包资产和资源组。例如,您可能希望允许用户发布包的配置文件,而不必强制发布包的资产。您可以通过在包的服务提供者的 boot 方法中调用 publishes 方法时“标记”它们来做到这一点。例如,让我们使用标签为 courier 包定义两个发布组(courier-configcourier-migrations):

php
/**
 * 启动任何包服务。
 */
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 命令时引用其标签来单独发布这些组:

shell
php artisan vendor:publish --tag=courier-config