在 Laravel 7.0 為關聯模型建立 Seeder 資料填充

前言

本文以「一個使用者擁有多個專案和多筆文章」為例,為關聯模型建立資料填充。

關聯方法

首先,為 User 模型建立 projects()posts() 關聯方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Get all of the projects for the user.
*
* @return \Illuminate\Database\Eloquent\Relations\hasMany
*/
public function projects() {
return $this->hasMany(Project::class);
}

/**
* Get all of the posts for the user.
*
* @return \Illuminate\Database\Eloquent\Relations\hasMany
*/
public function posts() {
return $this->hasMany(Post::class);
}

做法一

以父模型為主,由上而下建立關聯資料。

首先,建立一個 UserSeeder 資料填充,建立 5 個使用者,為每個使用者建立 10 個專案和 10 筆文章。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use App\Post;
use App\Project;
use App\User;
use Illuminate\Database\Seeder;

class UserSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
factory(User::class, 5)->create()->each(function ($user) {
$user->projects()->saveMany(factory(Project::class, 10)->make());
$user->posts()->saveMany(factory(Post::class, 10)->make());
});
}
}

此方法將所有模型都寫在同一個檔案,在互相關聯的情況下,很容易寫出複雜的巢狀結構,不易維護。

做法二

將模型工廠生成的測試資料儲存在靜態屬性中,讓測試資料可以在不同的 Seeder 之間分享。

首先,在 App\Traits 資料夾建立一個 HasStaticAttributes 特徵機制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
trait HasStaticAttributes
{
/**
* @var array
*/
private static array $attributes = [];

/**
* @param string $key
* @param mixed $value
* @return void
*/
private function setAttribute(string $key, $value)
{
self::$attributes[$key] = $value;
}

/**
* @param string $key
* @return array
*/
private function getAttribute(string $key)
{
return self::$attributes[$key] ?? null;
}

/**
* @param string $key
* @param mixed $value
* @return void
*/
public function set(string $key, $value)
{
$this->setAttribute($key, $value);
}

/**
* @param string $key
* @return mixed
*/
public function get(string $key)
{
return $this->getAttribute($key);
}

/**
* @param string $key
* @param mixed $value
* @return void
*/
public function __set(string $key, $value)
{
$this->setAttribute($key, $value);
}

/**
* @param string $key
* @return mixed
*/
public function __get(string $key)
{
return $this->getAttribute($key);
}

/**
* @param $method
* @param $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
return $this->getAttribute($method);
}
}

建立一個 UserSeeder 資料填充,引入特徵機制,建立 5 個使用者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use App\User;
use App\Traits\HasStaticAttributes;
use Illuminate\Database\Seeder;

class UserSeeder extends Seeder
{
use HasStaticAttributes;

private const AMOUNT = 5;

/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$this->users = factory(User::class, self::AMOUNT)->create();
}
}

建立一個 ProjectSeeder 資料填充,引入特徵機制,從 UserSeeder 取得測試資料,並為每個使用者建立 10 個專案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use App\Project;
use App\Traits\HasStaticAttributes;
use Illuminate\Database\Seeder;

class ProjectSeeder extends Seeder
{
use HasStaticAttributes;

private const AMOUNT = 10;

/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
app(UserSeeder::class)->users->each(function ($user) {
$user->projects()->saveMany(factory(Project::class, self::AMOUNT)->make());
});
}
}

建立一個 PostSeeder 資料填充,引入特徵機制,從 UserSeeder 取得測試資料,並為每個使用者建立 10 筆文章。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use App\Post;
use App\Traits\HasStaticAttributes;
use Illuminate\Database\Seeder;

class PostSeeder extends Seeder
{
use HasStaticAttributes;

private const AMOUNT = 10;

/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
app(UserSeeder::class)->users->each(function ($user) {
$user->posts()->saveMany(factory(Post::class, self::AMOUNT)->make());
});
}
}

此方法避免將所有模型都寫在同一個檔案內,而是利用靜態屬性分享測試資料,靈活度比較高。