LoginSignup
7
4

More than 1 year has passed since last update.

Laravel8.42で追加された、Has One Of Many のリレーション機能が便利

Posted at

概要

  • 2021/05/18 にリリースされた、Laravel v8.42.0 で Eloquent の Has One Of Manyという機能がリリースされました。
  • Eloquentの hasOne では、1:1 のリレーションを定義できますが、
  • 1:N のリレーション関係のテーブルに対して、指定条件で擬似的に、1:1を定義できる機能が追加されたのです!
  • 便利!やった!\(^o^)/

公式ドキュメント

試して見る

  • usersorders テーブルを用意します。
  • users テーブルを起点に、ユーザーの注文履歴を Has One Of Many を活用して様々な形で取得してみましょう。
MySQL [docker]> select id, name from users where id = 1;
+----+---------------+
| id | name          |
+----+---------------+
|  1 | Rowland Sipes |
+----+---------------+
1 row in set (0.001 sec)
MySQL [docker]> select id, user_id, price, paid_at from orders where user_id = 1;
+----+---------+-------+---------------------+
| id | user_id | price | paid_at             |
+----+---------+-------+---------------------+
|  2 |       1 |  1718 | NULL                |
|  8 |       1 |  6117 | NULL                |
| 45 |       1 |  6671 | NULL                |
| 46 |       1 |  1562 | NULL                |
| 71 |       1 |  3806 | NULL                |
| 73 |       1 |  5038 | 2021-05-27 16:21:23 |
| 74 |       1 |  5994 | 2021-05-27 16:21:23 |
| 85 |       1 |  2239 | 2021-05-27 16:21:23 |
| 89 |       1 |  5898 | 2021-05-27 16:21:23 |
+----+---------+-------+---------------------+
9 rows in set (0.001 sec)

以下のように Userモデルに Has One Of Many の リレーションを定義します。

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    public function latestOrder()
    {
        // max(id) の 条件で 1件取得する。
        return $this->hasOne(Order::class)->latestOfMany();
    }

    public function oldestOrder()
    {
        // min(id) の 条件で 1件取得する。
        return $this->hasOne(Order::class)->oldestOfMany();
    }

    public function largestOrder()
    {
        // max(price) の 条件で 1件取得する。
        return $this->hasOne(Order::class)->ofMany('price', 'max');
    }

    public function unpaidLatestOrder()
    {
        // 未払い(paid_at is null) かつ max(id) の 条件で 1件取得する。
        return $this->hasOne(Order::class)->ofMany([
            'id' => 'max',
        ], function ($query) {
            $query->whereNull('paid_at');
        });
    }

}

使ってみる

  • orders の SQLでの SELECT結果と
  • LaravelのEloquentのリレーション経由での取得結果を比べて頂ければわかりますが、期待通りの結果が得られました!
Route::get('/', function () {

    dump(User::find(1)->latestOrder()->first());
    // max(id) の 条件で 1件取得
    //    "id" => 89
    //    "user_id" => 1
    //    "price" => 5898
    //    "paid_at" => "2021-05-27 16:21:23"


    dump(User::find(1)->oldestOrder()->first());
    // min(id) の 条件で 1件取得
    //    "id" => 2
    //    "user_id" => 1
    //    "price" => 1718
    //    "paid_at" => null

    dump(User::find(1)->largestOrder()->first());
    // max(price) の 条件で 1件取得
    //    "id" => 45
    //    "user_id" => 1
    //    "price" => 6671
    //    "paid_at" => null

    dump(User::find(1)->unpaidLatestOrder()->first());
    // 未払い(paid_at is null) かつ max(id) の 条件で 1件取得
    //    "id" => 71
    //    "user_id" => 1
    //    "price" => 3806
    //    "paid_at" => null
});

あとがき

  • Laravelは、新しい機能がどんどんリリースされているので、最新に追随していくのが良いですね。
  • この利用シチュエーション結構あると思います。とても簡単で、直感的で使いやすいですね!
7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4