Laravel ORM 的新功能,One of Many

程式技術
sharkHead

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

參考資料

sharkHead
written by
sharkHead

後端打工仔,在下班後喜歡研究各種不同的技術。稍微擅長 PHP,並偶爾涉獵前端開發。個性就像動態語言般隨興,但渴望做事能像囉嗦的靜態語言那樣嚴謹。

0 則留言