PHPでチェックボックスの値を処理する際、毎回忘れて迷うのでメモ。最適解かどうかは不明。記述はLaravel、リレーション多対多を想定。
値を配列に整理して、array_diffで振り分けるところを押さえれば、他にも使えそう。
課題
フォームで送信されたデータを保存する時、テキストやラジオボタンと比べてチェックボックスは手間がかかる。
テキストやラジオボタンは値が一つしかないのに、チェックボックスは基本的に複数の値が送信されるのが原因。
フォーム
☐a ☐b ☐c ☐d ☐e ☐f ☐g ☐h [保存]
html
<form method="post" action="{{ route('save') }}">
@csrf
<input type="checkbox" name="hoge[]" value="a">a
<input type="checkbox" name="hoge[]" value="b">b
<input type="checkbox" name="hoge[]" value="c">c
<input type="checkbox" name="hoge[]" value="d">d
<input type="checkbox" name="hoge[]" value="e">e
<input type="checkbox" name="hoge[]" value="f">f
<input type="checkbox" name="hoge[]" value="g">g
<input type="checkbox" name="hoge[]" value="h">h
<button type="submit">保存</button>
</form>
雑な方法
php
// ユーザーを取得
$user = Auth::user();
// 保存されている関係をすべて解除
$user->hoges()->detach();
// 改めてチェックされたものを保存
$user->hoges()->attach($request->hoge);
この方法は簡単でいいのだけど、何も変更していなくても全消しして再保存となるので、なんとなく気持ち悪いし、プログラム的にもアンチパターンな気がする。
(おそらく)ちゃんとした方法
手順としては、チェックされたものと保存済みのものを照合して、保存するものと削除するものを振り分ける。
保存・削除ルール
チェックあり+未保存=保存
チェックあり+保存済み=スルー
チェックなし+保存済み=削除
チェックなし+未保存=スルー
例
フォームでチェックしたもの...[a b c d]
既にDBに保存済みのもの...[c d e f]
a b 保存(チェックあり+未保存)
c d スルー(チェックあり+保存済み)
e f 削除(チェックなし+保存済み)
g h スルー(チェックなし+未保存=スルー)
array_diffを使う
判定するには、array_diffを使います。
配列の取得
php
// チェックされた値
$checked = $request->hoge; //["a", "b", "c", "d"];
// 保存されている値
$saved = $user->hoges()->pluck('value')->toArray(); //["c", "d", "e", "f"];
保存するもの、削除するものに振り分け
php
// チェックされている値から保存されている値を除く
$save_array = array_diff($checked, $saved); //["a", "b"]
// 保存されている値からチェックされている値を除く
$del_array = array_diff($saved, $checked); //["e", "f"]
保存、削除
php
// 保存(attach)
if(!empty($save_array) && is_array($save_array)){
$user->hoges()->attach($save_array);
}
// 削除(detach)
if(!empty($del_array) && is_array($del_array)){
$user->hoges()->detach($del_array);
}
こうすれば、保存するべきものは保存され、削除するべきものは削除、それ以外のものは何もしない、と
「何も変更していなくても全消しして再保存」という処理を走らせなくて済む。