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>
めでたし、めでたし☺