Laravelでチェックボックスを使うときに覚えておくといいこと

Laravelでチェックボックスを実装するためのベストプラクティスをご紹介します。これを読めば、もうチェックボックスの実装に悩む必要はありません。

モデル構造

例えば、User モデルに以下のようなリレーションを定義してみましょう。ユーザーには「作成者(Author)」と「編集者(Editor)」といった複数の役割を割り当てることができます。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class User extends Model
{
    /**
     * ユーザーに属するロール
     */
    public function roles(): BelongsToMany
    {
        return $this->belongsToMany(Role::class);
    }
}

ルート定義

routes/web.phpファイルに以下のルートを定義してあるとしましょう。

use App\Http\Controllers\UserController;

Route::get('/users/{user}', [UserController::class, 'edit'])->name('user.edit');
Route::put('/users/{user}', [UserController::class, 'update'])->name('user.update');

GETのルートはユーザーの役割を編集するフォームを表示し、PUTルートで変更内容をデータベースへ保存します。

コントローラ実装

次に、これらのルートへの受信リクエストを処理するコントローラを見てみましょう。editメソッドではデータベースからすべての役割を取得してビューへ渡しています。

<?php

namespace App\Http\Controllers;

use App\Models\User;
use App\Models\Role;
use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
     * ユーザー編集画面の表示
     */
    public function edit(User $user)
    {
        $roles = Role::all();

        return view('user.edit', ['user' => $user, 'roles' => $roles]);
    }

    /**
     * ユーザー情報の更新
     */
    public function update(Request $request, User $user)
    {
        $request->validate(['roles' => 'array|exists:roles,id']);

        $user->roles()->sync($request->roles);

        return redirect()->route('user.edit', ['user' => $user]);
    }
}

チェックボックス表示

ビューでは渡された役割データをループ処理してチェックボックスを表示できます。今のところ、checked属性は設定していません。

<!-- /resources/views/user/edit.blade.php -->

<form action="{{ route('user.update', ['user' => $user]) }}" method="POST">
    @csrf
    @method('PUT')

    @foreach ($roles as $role)
        <div class="form-check">

            <input
                id="role{{ $role->id }}"
                type="checkbox"
                name="roles[]"
                @class(['form-check-input', 'is-invalid' => $errors->has('roles')])
                value="{{ $role->id }}"
            >

            <label for="role{{ $role->id }}" class="form-check-label">
                {{ $role->name }}
            </label>

            @if ($loop->last && $errors->has('roles'))
                <div class="invalid-feedback">{{ $errors->first('roles') }}</div>
            @endif

        </div>
    @endforeach

    <button class="btn btn-primary">保存</button>
</form>

チェック済み属性

それでは、チェック済み属性を設定してみましょう。チェック済みの判定結果を、ビューへ渡すデータに付け足すようにします。

/**
 * ユーザー編集画面の表示
 */
public function edit(Request $request, User $user)
{
    $roles = Role::all()->keyBy('id')->toArray();
    foreach (array_keys($roles) as $role_id) {
        $roles[$role_id]['checked'] = false;
    }
    foreach ($request->old('roles', $user->roles->modelKeys()) as $role_id) {
        if (isset($roles[$role_id]['checked'])) {
            $roles[$role_id]['checked'] = true;
        }
    }
    return view('user.edit', ['user' => $user, 'roles' => $roles]);
}

これで、ビューでは渡されたデータのフラグを確認するだけでcheckedの判別ができるようになりました。

<!-- /resources/views/user/edit.blade.php -->

<form action="{{ route('user.update', ['user' => $user]) }}" method="POST">
    @csrf
    @method('PUT')

    @foreach ($roles as $role)
        <div class="form-check">

            <input
                id="role{{ $role['id'] }}"
                type="checkbox"
                name="roles[]"
                @class(['form-check-input', 'is-invalid' => $errors->has('roles')])
                value="{{ $role['id'] }}"
                @checked($role['checked'])
            >

            <label for="role{{ $role['id'] }}" class="form-check-label">
                {{ $role['name'] }}
            </label>

            @if ($loop->last && $errors->has('roles'))
                <div class="invalid-feedback">{{ $errors->first('roles') }}</div>
            @endif

        </div>
    @endforeach

    <button class="btn btn-primary">保存</button>
</form>

めでたし、めでたし☺

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です