概要
- 2021/05/18 にリリースされた、Laravel v8.42.0 で Eloquent の Has One Of Manyという機能がリリースされました。
- Eloquentの hasOne では、1:1 のリレーションを定義できますが、
- 1:N のリレーション関係のテーブルに対して、指定条件で擬似的に、1:1を定義できる機能が追加されたのです!
- 便利!やった!\(^o^)/
公式ドキュメント
- ごちゃごちゃ書くより公式ドキュメントを見るのが早そうなので貼っておきます。
- 公式ドキュメント(英語版)
- Laravel News
試して見る
-
users
とorders
テーブルを用意します。 - 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は、新しい機能がどんどんリリースされているので、最新に追随していくのが良いですね。
- この利用シチュエーション結構あると思います。とても簡単で、直感的で使いやすいですね!