Laravel 的模型工厂使用小结

file
Laravel 5.1 之后新增了一个名为模型工厂的功能,用来快速构建一个「假」的模型。

让我们构建一个小小的应用程序来深入了解一下这个功能。这些有两个最大的测试和数据填充的用例。

开始

现在假设我们要做一个简单的报修系统,用来收集用户的问题反馈。报修的用户使用自带的 users 表,然后再创建一个新的问题表。

安装一个新的应用程序:

laravel new support

接下来,用 artisan 命令来创建我们的问题表的模型和迁移。 使用 make:model 命令传递 -m 或 -migration 来创建。

php artisan make:model Issues -m

这条命令会在 app/ 中生成 Issues.php 和 database/migrations/ 中生成 create_issues_table 迁移文件。

现在我们要在 app/User.php 模型文件中关联用户表与问题表之间的一对多关系:

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

如果你不想使用默认的配置,就编辑 .env 并更改数据库信息。

现在我们来填写这些迁移文件。

创建迁移

打开 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();
  });
}

现在运行迁移:

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

接下来,我们来填充数据,以便我们有数据来进行其他操作。

建立数据填充

数据填充是一种用程序运行的方式将数据插入到数据库中,其优点就是可以将虚拟数据快速地导入到应用程序中。 可以节省不少捏数据的时间呢!

先为用户表填充数据。 用以下命令生成编写数据填充的文件:

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 模型作为 define() 的第一个参数,然后定义了字段中的数据的回调。 这个回调中引入了 Faker,它是一个会生成假数据的 PHP 库,功能强大,可用于多种不同的场类型。 这是一个示例:

  • $fake->name – “John Smith”
  • $faker->email – “tkshlerin@collins.com”

注意:如果你的程序要发送真实的电子邮件,那就要使用 $faker->safeEmail。 原因是 $faker->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 类,在数据库中创建两个用户。然后用 collection 的 each 方法为每个被创建的用户建立关联的问题数据。

运行迁移填充的命令:

$ 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 模型工厂进行测试

拥有模型工厂的主要好处是我们可以在测试套件中使用这些。 为我们的问题创建一个新的测试:

php artisan make:test IssuesTest

打开 tests/IssuesTest.php 并添加一个新的方法来测试问题的创建:

<?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 trait,以便每个测试都被包装在一个事务中。我们在 testIssueCreation 中写测试添加问题的功能。 create 方法传递关联数组,能覆盖原始模型工厂定义中存储的内容。然后我们使用 seeInDatabase 去搜索数据库中是否存在给定的字段和值。

看到这里你应该知道怎么用模型工厂去测试和填充数据了吧?不懂就翻回去多看几遍,懂了的话,来看看其他一些进阶的功能!

其他特点

现在有件尴尬的事情,就是我忘了在数据库中添加一个字段用于写入问题的解决方法。

多工厂类型

在为这个字段 completed 添加新的迁移之后,我们需要对模型工厂进行调整,定义一个这样的新工厂:

$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 VS create

你可能已经注意到,上面调用了 ->make() 而不是 ->create()。 这两种方法做了两件不同的事情:create 尝试将其存储在数据库中,跟 Eloquent 中的 save 方法一样。 而 make 仅仅只是创建了模型,不会向数据库插入数据。 如果你熟悉 Eloquent,make 执行起来就像这样:

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

扩展

在 Laravel 5.3.17 中你可以在模型工厂定义不同的「状态」。

现在我们要为用户定义身份:

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

这个用户可以是管理员:

$factory->state(\App\User::class, 'admin', function (\Faker\Generator $faker) {
  return [
    'is_admin' => 1,
  ];
});

或者是主席:

$factory->state(\App\User::class, 'moderator', function (\Faker\Generator $faker) {
  return [
    'is_moderator' => 1,
  ];
});

然后你可以这样调用:

// Create 5 users
factory(\App\User::class, 5)->create();

// Create 5 Admins
factory(\App\User::class, 5)->states('admin')->create();

// Create 5 Moderators
factory(\App\User::class, 5)->states('moderator')->create();

// Create 5 Admins that are also moderators
factory(\App\User::class, 5)->states('admin', 'moderator')->create();

本文翻译改编自 Eric L. BarnesLearn to use Model Factories in Laravel

本作品采用《CC 协议》,转载必须注明作者和本文链接
Stay Hungry, Stay Foolish.
本帖由系统于 6年前 自动加精
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 2

在运行命令 php artisan migrate --seed 时,会报如下错误:

  [Symfony\Component\Debug\Exception\FatalErrorException]
  Class 'issues' not found

需要将 app/User.php中模型文件关联用户表与问题表的一对多关系改为:

public function issues()
{
    return $this->hasMany('App\Issues');
}
6年前 评论

刚好在写公司项目需要填充一下数据,记得以前翻译过类似的。找了好久才找到:joy: 现在重新发上来存着方便以后查看

6年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!