Database Seeding

PHP:7.2

Laravel:5.8

在撰寫專案的測試時,最苦惱的問題莫過於還要自行在 DB 建立相關的測試資料,Laravel 為了解決這個問題,提供了 Seed 類別協助匯入測試資料。

Generating Seeders

首先,需要先透過 artisan 自動建立 seeder 檔案,此檔案將會紀錄我們匯入資料的設定:

php artisan make:seeder MemberTableSeeder

建立完成以後,就可以在 database/seeds 中發現 MemberTableSeeder.php 的檔案。

檔案裡面只會有一個 run() 的 method,當執行指令時,就會依照該 method 裡面的操作進行資料匯入。

範例

use Illuminate\Database\Seeder;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\DB;

class MemberTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        // 一次建立 10 筆
        for ($i = 1; $i <= 10; $i++) {
            // 透過 DB class 建立資料
            DB::table('member')->insert([
                'label_id' => rand(1, 100),
                'name' => Str::random(10), // 產生長度 10 的字串
                'password' => bcrypt('secret'), // 產生亂數密碼
                'email' => Str::random(10) . '@gmail.com',
                'created_at' => time()
            ]);
        }
    }
}

Notice: 產生亂數字串的使用方法在 Laravel 不同的版本中都略有差異,使用前記得先參考該版本的文件 Helpers

Using Model Factories

除了使用 DB class insert 資料之外,也可以使用 Factory 搭配 Eloquent Model 定義 directory 以供 Seeder 使用。

另外, Factory 是使用 Facker 這個套件協助建立測試資料,因此在資料操作上會方便許多。

建立 Factory

php artisan make:factory MemberFactory

設定 MemberFactory (database/factories)

use Faker\Generator as Faker;
use Illuminate\Support\Str;
use App\Models\Member;

$factory->define(Member::class, function (Faker $faker) {
    return [
        'label_id' => Label::all()->random()->id, // 搭配 Model 匯入關聯的 ID
        'name' => $faker->name,
        'password' => bcrypt('secret'),
        'email' => $faker->unique()->safeEmail,
        'created_at' => time()
    ];
});

Notice: 相關 Faker 操作可以參考官方文件

Setting Seeders

在設定完成以後,我們就要執行資料的匯入,artisan 預設會去執行 database/seeds/DatabaseSeeder.php,因此我們在寫好 Seeders 後,還要在 DatabaseSeeder 控制呼叫的邏輯。

DatabaseSeeder.php

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        // 呼叫剛剛寫好的 Seeder
        $this->call(MemberTableSeeder::class);

        // 使用 Factory
        // 一次建 50 筆資料
        factory(App\Models\Member::class, 50)->create();
    }
}

Import Data

當設定都完成了之後,就可以使用 artisan 進行匯入:

php artisan db:seed

# 或者可以直接指定 Seeder 匯入
php artisan db:seed --class=MemberTableSeeder

Create Relation Data

Factory 因為是搭配 Model 使用,因此也可以建立關聯性的資料。

以下例子為建立 Member 的資料時同時建立 Post 資料:

MemberFactory

use Faker\Generator as Faker;
use Illuminate\Support\Str;
use App\Models\Member;
use App\Models\Label;

$factory->define(Member::class, function (Faker $faker) {
    return [
        'label_id' => Label::all()->random()->id,
        'name' => $faker->name,
        'password' => bcrypt('secret'),
        'email' => $faker->unique()->safeEmail,
        'created_at' => time()
    ];
});

PostFactory

use Faker\Generator as Faker;
use Illuminate\Support\Str;
use App\Models\Member;
use App\Models\Post;

$factory->define(Post::class, function (Faker $faker) {
    return [
        'member_id' => Member::all()->random()->id,
        'title' => $faker->sentence,
        'content' => $faker->paragraph,
    ];
});

Seeder

// 注意,因為是 Relation 資料,Model 需先建立 Member -> Post 的關聯
factory(App\Models\Member::class, 50)->create()->each(function ($member) {
    $member->posts()->save(factory(App\Models\Post::class)->make());
});
Categories: Laravel