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 命令,以快速构建 CommandsRequestsJobsEvents 等。
  • 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 目录,它包含了一个 laravelsrc 目录。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 提供了一系列的服务提供者,例如 AppServiceProviderAuthServiceProviderBroadcastServiceProviderEventServiceProviderRouteServiceProvider。这些服务提供者负责「引导」(或是「注册」)应用的服务(作为服务容器绑定),事件监听,中间件和路由。
每一个服务提供者都继承自 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 协议》,转载必须注明作者和本文链接
Pompeo is a pig.
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 2

什么时候更新?

9个月前 评论
Annlix

@VictorWang 工作之余才能继续更新

9个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
开发者 @ YNII · BUAA
文章
1
粉丝
1
喜欢
5
收藏
7
排名:1905
访问:401
私信
所有博文