😶‍🌫️

boolと文字列"0"の型変換で罠にはまる

2025/01/31に公開

やろうとしたこと

クエリパラメータで渡ってきた値を、Laravelで条件分岐に使おうと思っていた

引っかかったところ

1:jsでajaxで送信するとき、falseで送信した値をLaravelで処理しようとしたら、なぜか常にtrueになってしまう

2:falseを文字列"0"として送るようにしたら、Laravelでは文字列が来てるはずなのに、なぜかbooleanのfalseとして判定される
そのせいで!empty()が通らなくなった。

解決方法

真偽値は文字列に変換してからクエパラにいれる
empty判定が必要なものに文字列"0"を使わない

解説

なぜ引っかかっていたのか

送信側

    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";
        // 略
    }

何で引っかかってたのか
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のフラグがあるので、未設定の場合もカバーできる)

まとめ

「変換」には気をつけよう!
バグ調査はまず仕様をよく読むのが大事(痛感)

DELTAテックブログ

Discussion