0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Laravel】複数ユーザーの「ブロック一括解除」を実装する(whereIn + Alpine.js)

Posted at

はじめに

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句について

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?