前面介紹過Model、Repository,或許讀者會好奇這兩者的差異
首先,當然Repository不是Laravel預設有的,而實際上,也是有分 用/不用Service/Repository派
支持派認為,Service/Repository可以做到分層清楚、重用性高
反對派認為,Model可以用scope、trait達到同樣效果
不過這些都是題外話
今天一樣用先前使用Post的例子
還記得在創建Post有預留一個user_id嗎
假設今天的登入的user_id = 1
PS:可以透過以下mysql語法所有post的user_id
UPDATE posts
SET user_id = 1;
接著可以新增user
INSERT INTO `ithelp`.`users` (`id`, `name`, `email`, `email_verified_at`, `password`) VALUES ('1', 'ryan', 'ryan@test.com', '2023-10-02 19:50:45', 'none');
以上資料都是測試用
接著在Model加入Relation
app\Models\Post.php
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
app\Http\Controllers\PostController.php
public function index(PostService $postService) {
$posts = Post::with('user')->get();
return response()->json(['data' => $posts], 200);
}
Postman測試:
可以看到每個Post很方便又快速的取得了發文者的資料
當然如果你要取得user的全部發文也可以
app\Models\User.php
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
在你需要的地方
User::with('posts')->get();
belongsTo、hasMany之外還有belongTo、hasOne等等
這取決於兩表之前的關係是一對多,誰一誰多
這個例子是Post屬於User,而User可以有多Post,可以以這個例子去思考你所需要的關係
其他的寫法
今天的例子剛好解決N+1 Query的問題
Post::with('user') 在 Laravel 中是一種解決 N+1 查詢問題的方法,它使用 Eager Loading(貪婪加載)來一次性加載所有需要的關聯數據,而不是在後續的迴圈中進行單獨的查詢。
以下的代碼則會造成N+1 Query 問題
$posts = Post::all(); // query 1次
foreach ($posts as $post) {
$user = User::find($post->user_id); // query N次
...
}
看到以上的程式可以發現,foreach因為跑了$posts的數量N次
$posts也是一次query,所以是N+1問題
而Post::with('user')則是以(類似)
SELECT * FROM posts;
SELECT * FROM users WHERE id IN (?, ?, ?, ...);
只需要兩次query就可以達到,複雜度下降一個維度,減少伺服器、mysql負擔
除了relation另外還有scope、trait等等,可以查看官網
你要把之前Repository邏輯寫在Model層也是可以,筆者之前經歷過中大型專案也有這麼做的
而筆者認為,Model有自帶的定位和方法,加入不是用這些方法有點像畫蛇添足
總而言之,今天用Model Realation的例子帶給讀者了解到Model的其中一種用法,也許能讓你了解到Model的定位及定義