boolと文字列"0"の型変換で罠にはまる
やろうとしたこと
クエリパラメータで渡ってきた値を、Laravelで条件分岐に使おうと思っていた
引っかかったところ
1:jsでajaxで送信するとき、falseで送信した値をLaravelで処理しようとしたら、なぜか常にtrueになってしまう
2:falseを文字列"0"として送るようにしたら、Laravelでは文字列が来てるはずなのに、なぜかbooleanのfalseとして判定される
そのせいで!empty()が通らなくなった。
解決方法
真偽値は文字列に変換してからクエパラにいれる
empty判定が必要なものに文字列"0"を使わない
解説
1
なぜ引っかかっていたのか
送信側
const categoryIds = $('#select_category').val();
const is_uncategorized = categoryIds.includes('0');
$.ajax({
method: 'get',
data: {
category_ids: is_include_uncategorized === "1" ? categoryIds.filter(id => id !== '0') : categoryIds,
is_uncategorized: is_include_uncategorized
},
method: 'get',
url: '/search'
}).done(function (data) {
//略
});
受信側
public function search(Request $request) {
$is_include_uncategorized = ($request->is_include_uncategorized ?? "false") === "true";
// 略
}
※これはお知らせを検索する画面
マルチセレクトのプルダウンが変更されたら、選択されたカテゴリーをajaxでエンドポイントに送信して、該当するお知らせだけを返す。
カテゴリの「未設定」が選択された場合のフラグをfalseで送ってLaravelで処理しようとしたら、なぜか常にtrueになってしまっていた。
どうしてこうなったのか
ajax通信で真偽値を突っ込んだら、JSONに変換されるときにfalseが"true"に変換されていた。
それをどう解決したのか
最初から文字列"0"/"1"にして送る。
送信側
const categoryIds = $('#select_category').val();
const is_include_uncategorized = categoryIds.includes('0') ? "1" : "0";
$.ajax({
method: 'get',
data: {
category_ids: is_include_uncategorized === "1" ? categoryIds.filter(id => id !== '0') : categoryIds,
is_uncategorized: is_include_uncategorized
},
method: 'get',
url: '/search'
}).done(function (data) {
//略
});
受信側
public function search(Request $request) {
$is_include_uncategorized = ($request->is_include_uncategorized ?? "0") === "1";
// 略
}
2
何で引っかかってたのか
category_idsは配列で、未設定が選択されると文字列"0"が入る。受信側でbladeファイルに渡すとき、文字列"0"がfalseとして判定されていた
送信側
const categoryIds = $('#select_category').val();
const is_include_uncategorized = categoryIds.includes('0') ? "1" : "0";
$.ajax({
method: 'get',
data: {
category_ids: is_include_uncategorized === "1" ? categoryIds.filter(id => id !== '0') : categoryIds,
is_uncategorized: is_include_uncategorized
},
method: 'get',
url: '/search'
}).done(function (data) {
//略
});
どうしてこうなった
PHPでは文字列"0"はfalsyとして判定されるため、!empty($category_ids)の中に書いた処理が実行されない。
empty() は本質的に !isset($var) || $var == false と同じことを簡潔に記述しているだけです。
https://www.php.net/manual/ja/function.empty.php#example-5469
どうやって解決した
頑張って取りまわす方法も考えたが、そもそも面倒なので選択されたカテゴリが未設定のみの場合は$category_idsを送信しないことにした。(1のフラグがあるので、未設定の場合もカバーできる)
まとめ
「変換」には気をつけよう!
バグ調査はまず仕様をよく読むのが大事(痛感)
Discussion