Laravel 的模型工廠使用小結

JokerLinly發表於2017-07-04

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.

相關文章