Laravel ORM 的新功能,One of Many
在 Laravel 8.42 版本中,新增了一個蠻有趣的語法 One of Many。可以用來找出一對多關聯中,再「多」裡面「最新」的那一筆紀錄,並建立一對一關聯。
簡單舉個例子,以部落格文章 (Post) 與留言回覆 (Reply) 舉例。
- 一篇文章,可以有很多留言回覆。
- 一個留言回覆只能屬於一篇文章。
文章與回覆是很明顯的一對多關聯。
因此在 Post Model 中,可以使用 hasMany()
去定義與 Reply Model 的關係。
public function replies()
{
return $this->hasMany(Reply::class);
}
而新推出的方法「One of Many」,可以讓你取得與文章關聯的回覆中,最新的那一筆回覆紀錄,只需要在 Post Model 中先定義 One of Many。
public function latest_reply()
{
return $this->hasOne(Reply::class)->ofMany();
}
然後在需要找出文章最新回覆的時候,就可以使用 latest_reply
這個屬性。
use App\Models\Post;
// 取得 id 為 1 的文章
$post = Post::find(1);
// 該篇文章最新的回覆
$latestReply = $post->latest_reply;
你應該跟我一樣好奇,這個「最新」的定義是什麼呢?我們可以使用 toSql()
來看一下 SQL 的查詢語法。
$post->latest_reply()->toSql();
顯示的 SQL 查詢內容如下。
SELECT *
FROM `replies`
INNER JOIN
(SELECT MAX(id) AS id,
`replies`.`post_id`
FROM `replies`
GROUP BY `replies`.`post_id`) AS `latest_reply` ON `latest_reply`.`id` = `replies`.`id`
AND `latest_reply`.`post_id` = `replies`.`post_id`
WHERE `replies`.`post_id` = 1
AND `replies`.`post_id` IS NOT NULL
可以看到使用 MAX(id)
,意思就是找 id 最大的值,也就是「最新」,從原始碼中可以看到介面中定義的 ofMany()
其所攜帶的參數。
interface SupportsPartialRelations
{
/**
* Indicate that the relation is a single result of a larger one-to-many relationship.
*
* @param string|array|null $column
* @param string|Closure|null $aggregate
* @param string|null $relation
*/
public function ofMany($column = 'id', $aggregate = 'MAX', $relation = null);
/**
* Determine whether the relationship is a one-of-many relationship.
*
* @return bool
*/
public function isOneOfMany();
}
如果你覺得方法名稱 ofMany() 看不太出來是要找最新的話,One of Many 其實有提供比較清楚的寫法。
$this->hasOne(Login::class)->latestOfMany();
$this->hasOne(Login::class)->oldestOfMany();