Laravel Package 中文文档
该文翻译自 Laravel Package Development(laravelpackage.com/) 。本文中的第一人称代词均为原博作者。
译者注
由于译者能力有限,文章翻译可能存在纰漏、错误或不合理的地方,希望读者能够不吝指出。同时,由于译者需要搬砖养家糊口,文档翻译只能在工作之余的空闲时间完成,所以更新时间可能不固定,但译者会尽快完成文章翻译,还望见谅。
简介
以我的经验来看,学习开发一个 Laravel Package 是富有挑战的,这也就是为什么我之前写了一个 关于这个的博客系列 。
随着时间的推移,我开始认为这个主题应该有一个合适的文档,而不是几篇涉及我的见解的文章。这也就是这个关于 Laravel Package 开发的开源文档开始的地方。我已经对我的博客进行打包,并在不同的章节扩展了更多的内容。我非常欢迎您的贡献,您可以通过 PR 的形式参与其中。我希望这个网站能够成为分享 Laravel Package 开发知识的地方,并且能够帮助开发者开始进行相关工作。
我非常鼓励您参与并 为这个项目做出贡献,您可以随时提交 PR ,即便只是简单的打字错误。
首先,我想要感谢 Marcel Pociot 。他清晰明了、结构分明的 视频教程 鼓励我创建了自己的 PHP Packages 。
💡 您是否更青睐于看视频而不是看书?著名的包开发者 Spatie 发布了一个完整的 Laravel 包开发 的视频。
为什么要开发 Package
您可能会遇到这种情况:您希望在其他应用程序中复用应用程序的某些功能,开源部分功能,或是将相关的代码组织起来,但需要与主应用程序分开来。因此,将部分内容提取到 Package 就变得非常有意义。Package 或 library 提供了一个非常方便的途径来添加可选的功能到现有的应用中,并将重点放在了特定功能上。
配套包
在该文中,我们将会通过逐一介绍列出的功能来构建一个 demo Package(称之为 BlogPackage)。一定要检查这个 Package 的完成后的完整版本,以便有一个方便的参考,例如,当某些东西不能按照预期工作时。该 demo 中包含一个测试套件,包括针对所涵盖主题的单元测试和功能测试。
Composer 和 Packagist
在编写代码时,PHP 软件包的主要存储库 Packagist 上有近 240,000 个包可用。
您可以使用 Composer 来下载和安装它们,Composer 是 PHP 的包管理工具,它可以帮您管理项目的依赖。
要在一个既有的 Laravel 项目中安装包,您可以使用 composer require <vendor>/<package>
,它将为您下载所有必要的文件,并它们置于项目的 vendor
目录下,其中包含了所有的第三方软件包,并以 vendor 名称进行分隔。因此,这意味着包中的内容与应用程序代码分离,并且包中的代码由特定人员进行维护,通常是该包的创建者。
工具和助手
第一章将介绍 Package 的基本结构,了解 Package 的总体结构固然很好,但您亦可查看一下有用的工具,以立即设置基本框架。
- Package Skeleton by Spatie :Spatie的这个包为从头开始设置 Laravel Package 提供了一个很好的起点。除了 Laravel Package 软件包的基本组件外,该框架还提供了 Github 特定的配置,包括一组用于 Github 操作 (CI) 的工作流。它还提供了通用的 PHP 扩展包。
- Laravel Package Boilerplate :Marcel Pociot 的这个工具可以让您生成一个适用于 Laravel Package 和通用 PHP Package 的模板,您可以将其下载为一个
.zip
文件。 - Laravel Packager :Jeroen-G 的这个包提供了一个 CLI 的工具,可以从现有的 Laravel 应用中快速构建包。这个包在构建 Laracasts 系列的 Laracasts 有介绍。
- Laravel Packager Hermes :DelveFore 的这个包是 Laravel Package r包的一个扩展,它允许在该包中使用 Artisan 命令快速生成 Laravel 特定的类。目前,它仅支持
Controllers
的脚手架。 - Laravel Package Tools :和前面提到的包一样,Marcel Pociot 的这个包旨在从 Laravel 包中提供 Artisan 命令,以快速构建
Commands
,Requests
,Jobs
,Events
等。 - Orchestral Canvas :改包提供了代码生成器,以及复制了基本 Laravel 应用程序中的所有的
make
artisan 命令。 - Yeoman Laravel Package Scaffolder :这个包提供了一个独立的生成器来快速搭建 Laravel 包。它将生成一个骨架结构,一个现成的 composer.json 文件和一个完全配置的 Service Provider 。您只取消注释需要的内容并开始开发。
- Laravel Packer :一个PHP软件包,它提供了一个命令行工具来构建基本的包目录结构和
composer.json
文件,并在软件包中提供了make
artisan 命令。 - Laravel Package Maker :一个 PHP 软件包,提供所有 Laravel
make
命令以进行软件包开发。它使用 Composer 的存储库功能将您的测试应用程序与您的软件包符号链接,以使测试尽可能容易。
基础
自动加载
Composer 会在每次安装或更新包之后自动在 /vendor
目录下生成一个 autoload.php
文件。通过引用这个单一的文件,您可以访问您安装的包提供的所有类。
查看 Laravel 项目,您会发现在应用的根目录下的 public/index.php
文件(用于处理所有传入的请求)中包含了这个自动加载器(autoload.php
),这将使所有必需的库在您的应用程序范围内可用。其中包括 Laravel 的 Illuminate 组件以及所有必需的第三方软件包。
Laravel 的 public/index.php
文件:
<?php
define('LARAVEL_START', microtime(true));
require __DIR__.'/../vendor/autoload.php';
// additional bootstrapping methods...
目录结构
通常情况下(按照惯例),Package 包含一个 src/
(「source」的缩写)目录,其中包含了所有 package 特定的逻辑(类),以及一个 composer.json 文件,它包含了有关 package 的信息。此外,大多数软件包还包括许可证和文档。
如果我们查看一个 package 的通用目录结构,您会发现它与标准的 Laravel 有很大的区别:
- src
- tests
CHANGELOG.md
README.md
LICENSE
composer.json
在 package 中,所有置于 Laravel 项目的 app/
目录下的代码都位于 src/
目录下。
开发环境
安装 Composer
很有可能您已经安装了 Composer 。当然,如果您还没有安装 Composer ,那么最快地启动和运行的方式是复制 Composer 上提供的脚本。通过在命令行中复制和粘贴提供的脚本, 将会下载、安装和移除 composer.phar
安装器。您可以使用 composer self-update
来更新 Composer 到最新版本。
Package 架构
要开始开发一个 package ,首先,请创建一个空目录。无需将其嵌套在现有的 Laravel 项目中。为了清晰起见,我强烈推荐将您的 Package 与( Laravel )项目分开来。
例如,我将所有的 package 存储到 ~/packages
目录下,将我的 Laravel 应用存储到 ~/websites
目录下。
Composer.json
首先,在您的 package 根目录下创建一个 composer.json
文件,它拥有最小配置(如下所示)。请按需配置。name
最后与包名保持一致。标准约定是使用您的 Github / Gitlab / Butbucket / Gitee① 等,后根一个正斜杠(/),后接您的 kebab 格式的包名。
下面列出了 composer.json
的示例。
{
"name": "johndoe/blogpackage",
"description": "A demo package",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "John Doe",
"email": "john@doe.com"
}
],
"require": {}
}
或者,您可以通过在空目录下执行 composer init
来生成 composer.json
文件。
如果您打算公开发布您的 package ,那么选择合适 package 类型(在我们的例子中是: library )和许可证(同样的,在我们的例子中是:MIT)是很重要的。您可以访问 ChooseALicense.com 来选择一个合适的许可证。
命名空间
由于我们想要使用 src/
目录(约定俗成的)来存储我们的代码,因此我们需要告诉 Composer 在创建自动加载器( autoload.php
)时映射包的命名空间到制定的目录下。
我们可以按如下方式在 composer.json
文件中的 「psr-4」自动加载项下注册命名空间(请按需配置):
{
...,
"require": {},
"autoload": {
"psr-4": {
"JohnDoe\\BlogPackage\\": "src"
}
}
}
PSR-4 自动加载
现在,您可能已经明白了为什么我们需要一个「psr-4」键。 PSR 标准是 PHP Framework Interoperability Group ( PHP-FIG )设计的 PHP 标准建议。这个由 20 个成员组成的小组代表了 PHP 社区的一部分,他们提出了一系列的 PSR 。
在列表中,PSR-4 代表了一个关于从文件路径自动加载类的建议,取代了当时流行的 PSR-0 自动加载标准 。
PSR-0 和 PSR-4 之间显著的区别在于 PSR-4 允许将基本目录映射到特定的命名空间,因此可以在项目中使用较短的命名空间。我认为 Stackoverflow 上的 这个评论 清楚的描述了 PSR-0 和 PSR-4 是如何工作的。
PSR-0
"autoload": {
"psr-0": {
"Book\\": "src/",
"Vehicle": "src/"
}
}
它将在
src/Book/History/UnitedStates.php
文件中寻找Book\History\UnitedStates
它将在
src/Vehicle/Air/Wings/Airplane.php
文件中寻找Vehicle\Air\Wings\Airplane
PSR-4"autoload": { "psr-4": { "Book\\": "src/", "Vehicle\\": "src/" } }
它将在
src/History/UnitedStates.php
文件中寻找Book\History\UnitedStates
它将在
src/Air/Wings/Airplane.php
文件中寻找Vehicle\Air\Wings\Airplane
导入本地 Package
为了方便开发,您可以在本地的 Laravel 项目中导入位于本地 Package 。
如果您拥有一个本地的 Laravel 项目,您可以在 您的 Laravel 应用 的composer.json
文件中定义一个「repository」来自定义您的本地包。
在 Laravel 应用的composer.json
文件中的「scripts」部分下方添加「repositories」键(请将「url」的值修改为您的 package 所处的位置):{ "scripts": { ... }, "repositories": [ { "type": "path", "url": "../../packages/blogpackage" } ] }
现在,您便可以在您的 Laravel 项目中使用您的包名来引用你的包。按照我们的例子,这将是:
composer require johndoe/blogpackage
如果您在同一目录中有多个软件包,并且想让 Composer 查找所有软件包,则可以使用通配符
*
列出包的位置,如下所示:{ "scripts": { ... }, "repositories": [ { "type": "path", "url": "../../packages/*" } ] }
重点:每当您修改了 package 或其他注册了的任何程序的
composer.json
文件时,都需要在 Laravel 程序中执行composer update
。Orchestra Testbench
我们现在已经拥有了一个
composer.json
文件和一个空的 src/ 目录。但是,我们还无法访问Illuminate
组件提供的 Laravel 特定的功能。
要在包中使用这些组件,我们可以使用Orchestra Testbench 。请注意,每个版本的 Laravel 框架都有一个对应的 Orchestra Testbench 版本。在这一节中,我假设我们在 Laravel 8.0 下进行开发,这是撰写该部分时最新的 Laravel 版本。composer require --dev "orchestra/testbench=^6.0"
下方列出了完整的 Orchestra Testbench 兼容性列表,摘自其 原始文档 。
Laravel | Testbench |
---|---|
8.x | 6.x |
7.x | 5.x |
6.x | 4.x |
5.8.x | 3.8.x |
5.7.x | 3.7.x |
5.6.x | 3.6.x |
5.5.x | 3.5.x |
5.4.x | 3.4.x |
5.3.x | 3.3.x |
5.2.x | 3.2.x |
5.1.x | 3.1.x |
5.0.x | 3.0.x |
当 Orchestra Testbench 安装完成后,您可以找到 vendor/orchestra/testbench-core
目录,它包含了一个 laravel
和 src
目录。laravel
目录和真实的 Laravel 应用的结构类似,src
目录提供了 Laravel 助手函数,它涉及与项目目录的交互(例如:与文件操作相关的)。
在每次测试前,TestBench 会创建一个测试环境,其包含了一个完整的 booted (test) 应用。如果我们使用 Orchestra TestBench 的基础 TestCase
测试用例来进行测试,Orchestra\Testbench\Concerns
命名空间的 CreatesApplication
trait 提供的方法将负责创建测试应用。如果我们查看其中的一个方法 getBasePath()
,我们可以发现它直接指向了 Orchestra Testbench 的 laravel
目录。
// 'vendor/orchestra/testbench-core/src/Concerns/CreatesApplication.php'
/**
* Get base path.
*
* @return string
*/
protected function getBasePath()
{
return \realpath(__DIR__.'/../../laravel');
}
服务提供者
包的一个重要部分便是服务提供者。在创建一个服务器提供者前,我将在本节中说明服务提供者的含义。如果您已经很熟悉服务提供者,请继续阅读下一部分。
正如我们所知,Laravel 提供了一系列的服务提供者,例如 AppServiceProvider
,AuthServiceProvider
,BroadcastServiceProvider
,EventServiceProvider
和 RouteServiceProvider
。这些服务提供者负责「引导」(或是「注册」)应用的服务(作为服务容器绑定),事件监听,中间件和路由。
每一个服务提供者都继承自 Illuminate\Support\ServiceProvider
并且实现了一个 register()
和 boot()
方法。boot()
方法用于绑定服务容器中的内容。在所有其他服务提供者都完成注册后(也就是说:包括第三方包在内的所有服务器提供者的 register()
方法都被调用),Laravel 将调用所有服务提供者的 boot()
方法。
在 register()
方法中,您可以在服务容器中注册一个类绑定,从而可以从容器中解析一个类。但是,有时您可能需要引用另一个类,在这中情况下,您可以使用 boot()
方法。
下面是服务提供者的一个示例,以及您可以在 register()
和 boot()
方法中实现哪些内容。
use App\Calculator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
// 在服务容器中注册一个类
$this->app->bind('calculator', function ($app) {
return new Calculator();
});
}
public function boot()
{
// 注册一个宏,它继承自 Illuminate\Collection 类
Collection::macro('rejectEmptyFields', function () {
return $this->reject(function ($entry) {
return $entry === null;
});
});
// 注册一个验证机制
Gate::define('delete-post', function ($user, $post) {
return $user->is($post->author);
});
}
}
#
创建一个服务提供者
我们将为包创建一个服务提供者,其中包含了有关包核心的特定信息。包可能使用配置文件,也可能是视图、路由、控制器、数据库迁移、模型工厂、自定义命令等等。服务提供者需要注册它们。我们将在随后的章节中分别讨论它们。
由于我们已经拉取了 Orchestra Testbench ,我们可以向下方这样让在 src/
目录下创建的服务提供者继承 Illuminate\Support\ServiceProvider
(请按需替换):
// 'src/BlogPackageServiceProvider.php'
<?php
namespace JohnDoe\BlogPackage;
use Illuminate\Support\ServiceProvider;
class BlogPackageServiceProvider extends ServiceProvider
{
public function register()
{
//
}
public function boot()
{
//
}
}
自动加载
为了使用 Laravel 的包自动发现功能在 Laravel 项目中自动注册服务提供者,我们可以将服务提供者添加到包的 composer.json
文件的「extra」> 「laravel」>「providers」键中。
{
...,
"autoload": { ... },
"extra": {
"laravel": {
"providers": [
"JohnDoe\\BlogPackage\\BlogPackageServiceProvider"
]
}
}
}
现在,无论谁引用了我们的包,服务提供者都将会自动加载,并且在应用中都可以使用任何我们注册的内容。现在,让我们看看我们可能想要在服务提供者中注册什么。
① 译者注:原文没有 Gitee ,Gitee 为译者加入。
[内容持续更新中……]
本作品采用《CC 协议》,转载必须注明作者和本文链接
什么时候更新?
@VictorWang 工作之余才能继续更新