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. Barnes 的 Learn to use Model Factories in Laravel
本作品採用《CC 協議》,轉載必須註明作者和本文連結