Laravel 模型工厂入门

Laravel

从 Laravel 5.1 开始增加了一个叫做模型工厂的功能,主要为了允许开发者快速构建「假」模型。

这里有很多案例,其中两个最大的案例是测试案例和数据库种子案例。我们通过构建一个小的虚构的 App 的开端,来深入了解这一功能。

起始#

我们假设你被一个叫做 LEMON 的房屋定制建筑商雇佣,他们需要一个在客户房屋建成后提交故障单的方式。使用类似 Lemon 这样的名称,你知道他们会被问题淹没,因此你的任务就是简化这个流程。

一切从简,我们可以为客户创建用户表,再创建新的问题表。我们现在就着手把这些做出来。

创建一个新的应用:

laravel new lemon-support

接下来,我们使用 artisan 命令创建模型和迁移文件。通过使用 make:model 命令传递 m--migrate 标识。

php artisan make:model Issues -m

如果你打开 app/ 文件夹,你会看到 Issues.php 文件,然后在 database/migrations/ 文件夹会看到 create _ issues _ table migration 迁移文件中。

继续打开 app/User.php 模型文件并定义 issues 关系:

public function issues()
{
  return $this->hasMany('issues');
}

这是一个很好的时间编辑您的 .env 文件,如果你不想使用默认的 homestead,你可以改变你的数据库名称。

现在让我们填写迁移文件。

创建迁移#

打开 database/migrations/datetime_create_issues_table.php 将 up 方法改为如下内容:

public function up()
{
  Schema::create('issues', function (Blueprint $table) {
      $table->increments('id');
      $table->integer('user_id');
      $table->string('subject');
      $table->text('description');
      $table->timestamps();
  });
}

在这个迁移中,我们为 users 表添加了一个 user_id 关系,添加了一个问题的主题 subject,还有一个 description 可以让用户添加详细的报告信息。

运行这个迁移:

php artisan migrate

这会输出如下内容:

Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
Migrated: 2015_10_03_141020_create_issues_table

接下来我们要配置数据库的种子,这样我们就可以使用示例数据了。

构建数据库种子#

数据库种子 (seeds) 是一种以编程的方式将数据插入数据库的方式,使用种子的优点之一是,你可以快速地将虚拟数据放在应用程序中。这在处理用户界面或是在团队中都非常有用。

我们开始创建用户的种子。使用以下命令:

php artisan make:seeder UserTableSeeder

继续为问题创建种子:

php artisan make:seeder IssueTableSeeder

现在打开 database/seeds/DatabaseSeeder.php ,并将 run 方法改为以下内容:

public function run()
{
  Model::unguard();
  $this->call(UserTableSeeder::class);
  $this->call(IssueTableSeeder::class);
  Model::reguard();
}

在这些种子投入使用之前,还需要对插入内容的描述。我们需要使用模型工厂来实现。

创建工厂模型#

在过去创建种子数据时,我们可以使用 Eloquent 或者 Laravel 的查询构造器,这两种方法依然是支持的。然而,随着工厂模式的引用,你可以使用它们构建 “虚拟” 模型来用于创建种子数据和测试。

打开 database/factories/ModelFactory.php 文件,你会见到一个已经定义好的默认值:

$factory->define(App\User::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
        'password' => bcrypt(str_random(10)),
        'remember_token' => str_random(10),
    ];
});

为了解释这一点,我们定义 “App\User::class” 模型作为第一个参数,然后定义列数据的回调。这个回调同时注入了 Faker,这是一个用来生成虚假数据的 PHP 库。Faker 功能强大可以被用来定义不同类型的数据。这里有一个示例用来说明:

请注意: 如果你准备发送真正的电子邮件你应该使用 $faker->safeEmail 用来替代 ->email。原因是 ->email 可能会生成一封真正的电子邮件,safeEmail 使用 RFC2606 保留的 example.org 是用于测试发送电子邮件目的的。

现在让我们为问题模型创建一个工厂。这里是一段完整的代码:

$factory->define(App\Issues::class, function (Faker\Generator $faker) {
    return [
        'subject' => $faker->sentence(5),
        'description' => $faker->text(),
    ];
});

根据这个定义,我们用五个单词为主语生成了一个句子,最后用于描述的虚拟文本。

现在让我们切换到我们的种子类并且用这些工厂生成数据。

打开 UserTableSeeder 并运行 run() 方法:

public function run()
{
  factory(App\User::class, 2)->create()->each(function($u) {
    $u->issues()->save(factory(App\Issues::class)->make());
  });
}

这可能看起来势不可挡,所以让我们打破它。factory(App\User::class, 2)->create() 在英语中说,构建一个 User 类,我们需要 2 个用户,然后通过保存到数据库中来创建它们。

接下来是一个集合,每个集合将通过 each 创建的用户运行。最后,在每个保存与创建的用户相关的问题。

运行 artisan 命令:

$ php artisan migrate --seed
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
Migrated: 2015_10_03_141020_create_issues_table
Seeded: UserTableSeeder 

现在,我们的数据库将拥有我们的表,他们讲包含从模型工厂生产的示例数据。

与 Laravel 模型工厂一起测试#

拥有模型工厂的一个主要好处是我们现在可以在我们的测试环境中使用他们。为 Issues 创建一个新的测试。

php artisan make:test IssuesTest

打开 tests/IssuesTest.php 文件并添加一个新方法来测试 issue 创建:

<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class IssuesTest extends TestCase
{
    use DatabaseTransactions;
    public function testIssueCreation()
    {
      factory(App\Issues::class)->create([
          'subject' => 'NAIL POPS!'
      ]);
      $this->seeInDatabase('issues', ['subject' => 'NAIL POPS!']);
    }
}

在这个测试中,我们使用 DatabaseTransactions 特性,以便将每个测试包装在一个事务中。内部测试是我们的模型工厂增加了一个新的特点。Create 方法传递一个带有列名和预设值的数组。这允许您覆盖原始模型工厂定义中存储的内容。

在实际的测试中,我们使用 seeInDatabase,它搜索 DB 树,如果找到给定的列和值,则返回绿色。

现在我们已经看到了这些模型工厂是如何同时用于生产数据和测试的,让我们来看看它提供的一些有用的特性。

工厂模型的其他特性#

在准备与 LEMON 家的第一次会议期间,您注意到了原始设置中的一个问题。你忘了添加一种标记已解决问题的方法。那会很尴尬的。

多工厂类型#

在为 “已完成” 字段添加新的迁移之后,模型工厂需要进行调整,但是有一个单独的迁移将会很方便,这样您就可以轻松地完成一个已完成的问题。这样定义一个新工厂:

$factory->defineAs(App\Issues::class, 'completed', function ($faker) use ($factory) {
    $issue = $factory->raw(App\Issues::class);
    return array_merge($issue, ['completed' => true]);
});

此参数使用名称作为第二个参数,在本例中为「completed」,在回调中,我们创建了一个新问题,并将已完成列合并到其中。

如果您现在要创建一个「completed」问题,则所需的所有操作可以运用以下代码:

$completedIssue = factory(App\Issue::class, 'completed')->make();

模型工厂 make 对比 create#

在最后一个示例中,您可能已经注意到我调用了 ->make() 而不是像以前那样调用 ->create()。这两种方法做了两件不同的事情,「create」方法尝试将其存储在数据库中,这与在 Elount 中保存相同。另一方面,「make」方法只创建模型,但实际上不插入。如果您熟悉 Elount,则可与以下内容进行比较:

$issue = new \App\Issue(['subject' => 'My Subject']);

结束#

正如您所见,模型工厂是 Laravel 中的一个强大功能,有助于简化测试和数据填充。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://laravel-news.com/learn-to-use-mo...

译文地址:https://learnku.com/laravel/t/62393

本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。