BLOG

ブログ

2026/01/19 プログラミング技術系

【Laravel】最大/最小/最新/最古のリレーションデータを取得する

この記事を書いた人 T.S

Laravelでは、モデルクラスにhasOnehasManyなどの記載をすることで、テーブルデータ同士の関連付け(リレーション)を簡単に定義できます。
今回は、1つのデータに対して複数の関連データが存在する「1対多」リレーションを対象に、値が最大のもの1件や、最新のもの1件など、ソートしたデータを簡単に取得する方法をご紹介します!(1対多のリレーションについて詳しく知りたい方は、弊社メンバーのこちらの記事がオススメです!)

検証環境は以下の通りです。

  • MySQL:8.4.0
  • Laravel:11.34.1
  • PHP:8.3.14

最大/最小のリレーションデータを取得する

例として、店舗テーブル(shops)と商品テーブル(items)があり、店舗データ1件に対して、商品データが複数登録可能な「hasMany」と「belongsTo」の関係にあるとします。

1対多(hasMany)の関係の中から、特定のカラム値が最大/最小のものを取得するには、モデル内で hasOne()->ofMany() を使用します。

// 基本のリレーション(1対多)
// 店舗がもつ商品データを取得する
public function items()
{
    return $this->hasMany(Item::class);
}

// 店舗がもつ商品データで、値段が最大のものを1件取得する
public function biggestPriceItem()
{
    return $this->hasOne(Item::class)->ofMany('price', 'max');
}

// 店舗がもつ商品データで、値段が最小のものを1件取得する
public function lowestPriceItem()
{
    return $this->hasOne(Item::class)->ofMany('price', 'min');
}

hasOneにリレーション先モデルを指定し、ofMany の第一引数にカラム名、第二引数にmax または minを渡します。そうすると、hasMany の関係にあるリレーションデータの中から、指定したカラム値が最大/最小のものを1つに絞って取得することができます。

取得メソッドの呼び出し方は以下になります。

$shop = Shop::findOrFail($shopId);
$maxPriceItem = $shop::biggestPriceItem; // 値段が最大のものを1件取得
$minPriceItem = $shop::lowestPriceItem; // 値段が最小のものを1件取得

もちろん、「hasOne」と「belongsTo」を掛け合わせずに、hasMany をベースとした基本的な Eloquent だけでも取得可能です。
ただしこの方法は、毎回 orderBy()->first() を書く必要があるので、何度も取得する可能性がある場合は、モデルにメソッドを定義した方が再利用性・可読性ともに高いかなと思います。

$shop = Shop::findOrFail($shopId);
$maxPriceItem = $shop->items()->orderBy('price', 'desc')->first(); // 最大価格1件
$minPriceItem = $shop->items()->orderBy('price', 'asc')->first(); // 最小価格1件

備考:ofManyの引数について

ofMany() には配列を渡すことも可能で、複数カラムに対して 「どの値を優先するか」をまとめて指定できます。
下記の例では、2つの条件を組み合わせています。

  • price の最大値を優先
  • priceの最大値が同じ場合、 id の最小値を優先
// 店舗がもつ商品データで、値段が最大のものを1件取得する
// 値段の最大値が重複した場合、idが1番小さいものを取得する
public function biggestPriceWithMinIdItem()
{
    return $this->hasOne(Item::class)->ofMany([
        'price' => 'max',
        'id'    => 'min',
    ]);
}

最新/最古のリレーションデータを取得する

hasMany の中から最新の1件や最古の1件を取得したい場合は、 latestOfMany()oldestOfMany() が便利です。

// 店舗がもつ商品データで、最新のものを取得する(主キー基準)
// NOTE: 引数を指定していないので、主キー(ID)が最大のものを取得
public function latestItem()
{
    return $this->hasOne(Item::class)->latestOfMany();
}

// 店舗がもつ商品データで、最古のものを取得する(主キー基準)
public function oldestItem()
{
    return $this->hasOne(Item::class)->oldestOfMany();
}

// 店舗がもつ商品データで、更新日が最新のものを取得する(更新日(updated_at)基準)
public function latestItemByUpdatedAt()
{
    return $this->hasOne(Item::class)->latestOfMany('updated_at');
}

引数なしでlatestOfMany()oldestOfMany() を使用すると、「主キー(ID)」を基準に値を取得します。「作成日時(created_at)」や「更新日時(updated_at)」など、ID以外のカラムを基準にしたい場合は、引数にカラム名を指定します。

取得メソッドの呼び出し方は以下になります。

$shop = Shop::findOrFail($shopId);
$latestItem = $shop::latestItem; // 最新のものを1件取得(ID基準)
$oldestItem = $shop::oldestItem; // 最古のものを1件取得(ID基準)
$latestItemByUpdatedAt = $shop::latestItemByUpdatedAt; // 最新のものを1件取得(更新日基準)

(Laravel 10.x 以降) one()を使った書き方

Laravel10以降であれば、hasOne()->ofMany()の書き方だけでなく、既存のリレーションに対しone()->ofMany()を組み合わせる書き方も利用できます。
hasOne にモデル指定が不要となり、定義済みのhasManyリレーションを流用できるところが便利で良いポイントです👍

参考:“Many”リレーションをHas Oneリレーションへ変換する

// 基本のリレーション(1対多)
public function items()
{
    return $this->hasMany(Item::class);
}

// one()->ofMany()
public function biggestPriceItem()
{
    // return $this->hasOne(Item::class)->ofMany('price', 'max');
    return $this->items()->one()->ofMany('price', 'max');
}

// one()->latestOfMany()
public function latestItem()
{
    // return $this->hasOne(Item::class)->latestOfMany();
    return $this->items()->one()->latestOfMany();
}

おわりに

今回は、Laravelでソートしたリレーションデータを簡単に取得する方法をご紹介しました。

hasOne()->ofMany()latestOfMany()をモデルに定義するだけで、必要な場面でサッと呼び出せるのでとても便利です✨ 利用場面の多そうなソートデータがあれば、最初からモデルに定義をしておくと、コードの利便性や可読性向上に繋がるかと思います。

ご参考になれば幸いです🙇‍♂️


株式会社ウイングドアは福岡のシステム開発会社です。
現在、私達と一緒に"楽しく仕事が出来る仲間"として、新卒・中途採用を絶賛募集しています!
ウイングドアの仲間達となら楽しく仕事できるかも?と興味をもった方、
お気軽にお問い合わせ下さい!

アーカイブ