Laravel 8.42 で加わった withExists() を試す
前書き
タイトルの通り、Laravel 8.42 で加わったやや地味目な QueriesRelationships の withExists を試してみます。
これはどういう時に使ったりするかと言うと、例えば、管理者がユーザーの一覧を表示し、ブログ記事を投稿した事があるユーザーと無いユーザーを区別する際に役立ちます。
という事でこの記事では、ユーザーの一覧を表示し、まだブログ記事を作成してない人に「催促する」ボタンを表示する所を見て行きます。
ちなみに、以前からある withCount() を使っても実現できたりしますが、存在チェックするだけなので、カウントするよりも効率が良い、というのが売りです。
またもちろん、いわゆる N+1 問題も解消しています。
参考1:GitHub Add withExists method to QueriesRelationships
参考2:本家ドキュメント Other Aggregate Functions (詳しい説明は無し)
データの準備
以下のコマンドで、ブログ用のモデル、マイグレーション、ファクトリファイルを作ります。
php artisan make:model Post -mf
マイグレーションは、以下のように編集します。(titleとか今回は使用しませんが、一応)
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id');
$table->string('title');
$table->text('body');
$table->timestamps();
});
}
PostFactoryは、以下のような感じで。
public function definition()
{
return [
'title' => $this->faker->realText(10),
'body' => $this->faker->realText(30),
];
}
DatabaseSeederは、以下のように変更します。
use App\Models\Post;
use App\Models\User;
public function run()
{
User::factory(10)->create()->each(function ($user) {
Post::factory(random_int(0, 3))->create(['user_id' => $user]);
});
}
上記のSeederで、各ユーザーに対して、0~3件のブログ投稿を登録しています。
0件の場合は、まだブログを作成していない、という事ですね。
以下でマイグレーションとシーディングを実行します
php artisan migrate --seed
ファイル編集
Models/User に以下を追加します。
public function posts()
{
return $this->hasMany(Post::class);
}
web.php を以下のようにします。
<?php
use App\Models\User;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
$users = User::withExists('posts')->get(); // ← 今回のポイント1
return view('welcome', compact('users'));
});
views/welcome.php を以下とします。
<ul>
@foreach($users as $user)
<li>
{{ $user->name }} /
@if($user->posts_exists) <!-- 今回のポイント2 -->
ブログあり
@else
<button>催促する</button>
@endunless
</li>
@endforeach
</ul>
これをブラウザでアクセスした時、下記のようなイメージになります。
リレーションメソッド名_exists(今回の場合、posts_exists) とする事により、そのユーザーがブログを投稿しているか否かが分かります。(true / false で取得)
ちなみに、この一覧表示で発行されるSQLは、以下の1つのみです。
select `users`.*, exists(select * from `posts` where `users`.`id` = `posts`.`user_id`) as `posts_exists` from `users`
雑感
これを使用する機会に恵まれるといいですが、あまり無さそうです…。
おかしな箇所等あったら、コメント下さい。
Discussion