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();

 

參考資料
"One of Many" Eloquent Relationship Added to Laravel

sharkHead 後端工程師,稍微擅長 Laravel、Python 與 Google
對於前端有興趣,無奈沒什麼慧根