はじめに
Laravelでダイアリー共有アプリを制作している中で学んだことをまとめたものです。
どなたかのお役に立てれば幸いです。
本記事の動作環境
- Laravel 12.x
- Docker + Laravel Sailを使用
- macOS (M1)
実現したいこと
- ユーザーのブロック機能を実装中
- ブロックしているユーザー一覧画面からブロック解除できるようにする
- チェックボックスで複数ユーザーを選択し、一括解除できるようにしたい
前提
blocksテーブルの構造
create_blocks_table.php
public function up(): void
{
Schema::create('blocks', function (Blueprint $table) {
$table->id();
$table->foreignId('blocker_id')->constrained('users')->cascadeOnDelete();
$table->foreignId('blocked_id')->constrained('users')->cascadeOnDelete();
$table->timestamps();
// 同じ組み合わせで複数回ブロックされないようにする(重複登録の防止)
$table->unique(['blocker_id', 'blocked_id']);
$table->index('blocker_id');
$table->index('blocked_id');
});
}
Userモデルのリレーション
blocks()では、中間テーブルblocksを通して「ブロックしているユーザー一覧」を取得しています。
User.php
public function blocks()
{
return $this->belongsToMany(User::class, 'blocks', 'blocker_id', 'blocked_id');
}
実装内容
ブロックしているユーザーの一覧に「選択モード」を導入し、
ボタンをクリックすると、各ユーザーの左端にチェックボックスを表示するようにしています。
(コードを見やすくするために、CSSを省略して表示しています。)
resources/views/user_block/blocks.blade.php
<form method="POST" action="{{ route('blocks.bulk-destroy') }}" x-data="{ selectMode : false }">
@csrf
@method('DELETE')
<div>
<button type="button" @click="selectMode=!selectMode">
<template x-if="!selectMode"><span>選択</span></template>
<template x-if="selectMode"><span>キャンセル</span></template>
</button>
<button type="submit" x-show="selectMode">ブロック解除</button>
</div>
<ul>
@forelse($blocks as $user) {{-- $blocks には「ブロックしているユーザー一覧」が入っています --}}
<li>
<input type="checkbox" name="user_ids[]" value="{{ $user->id }}" x-show="selectMode">
<a href="{{ route('user.profile.show', $user) }}">
{{-- 省略 --}}
</a>
</li>
@empty
<li>ブロックしている人はいません。</li>
@endforelse
</ul>
</form>
- チェックボックス、ボタン類の表示、非表示は
Alpine.jsで制御 - チェックボックスでブロック解除するユーザーの
idを配列で取得
ルーティングは以下の通り。
routes/web.php
Route::delete('/blocks/bulk-destroy', [UserBlockController::class, 'bulkDestroy'])
->name('blocks.bulk-destroy');
コントローラで、送られてきたidのユーザーのブロックをwhereInを使って一括解除します。
UserBlockController.php
public function bulkDestroy(Request $request)
{
$request->validate([
'user_ids' => 'required|array',
'user_ids.*' => 'integer',
]);
// チェックされたユーザーID(空なら[])を配列で取得
$userIds = $request->input('user_ids', []);
$length = count($userIds);
Block::where('blocker_id', $request->user()->id)
->whereIn('blocked_id', $userIds)
->delete();
return back()
->with('status', $length . '人のブロックを解除しました。')
->with('status_type', 'success');
}
これで、ブロック解除の複数件一括処理がうまくいきました。
最初は以下のようにループ処理で書いていて、それでも動きましたが、whereInの方がシンプルだった上記のようにしました。
foreach($userIds as $userId) {
Block::where('blocker_id', $request->user()->id)
->where('blocked_id', $userId)
->delete();
}
おわりに
whereInを使うことでループを使わずシンプルに書けました。複数件の一括削除は、様々な場面で使うと思うので、覚えておくと便利ですね。
参考
- input
- where句について