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 并添加一个新的方法来测试问题的创建:
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();
本作品采用《CC 协议》,转载必须注明作者和本文链接
Stay Hungry, Stay Foolish.