Developer

【PHP応用】PDO⑱ WHEREが動的に変わる場合のサンプル
2021.09.30
Lv1

【PHP応用】PDO⑱ WHEREが動的に変わる場合のサンプル

PDO⑱ WHEREが動的に変わる場合のサンプル

WHEREが動的に変わる場合のサンプルシステムをご紹介します。
データは別の記事で使用しているものを使用します。
以下リンクの下部にある「動作環境について」の項目から環境を作成してください。
SQL練習問題 – 一覧まとめ

■要件

  • サッカー選手の一覧が表示される
  • 検索欄に名前を入力し検索すると、選手名の部分一致で検索ができる
  • 名前が入力されていない場合は、名前による条件なし
  • チェックを入れて検索すると、選択したポジションの選手が表示される
  • チェックを入れずに検索した場合は、ポジションによる条件なし
  • チェックは検索ボタンを押下後もチェックが状態が維持される
  • 名前とポジションの条件はAND条件である

▼名前に「ニョ」と入力し、ゴールキーパー(GK)とディフェンス(DF)にチェックを入れて検索ボタンを押した場合


▼名前は入力なしで、チェックを入れずに検索ボタンを押した場合


■プログラム

※デザインにBootstrap5を使用しています。
pdo_where.php

<?php
    try {

        // 接続処理
        $dsn = 'mysql:host=localhost;dbname=worldcup2014';
        $user = 'root';
        $password = '';
        $dbh = new PDO($dsn, $user, $password);


        $sql = <<<EOM
            SELECT
                p.id AS playerId
                , p.name AS playerName
                , c.name AS countryName
                , p.club
                , p.position
                , p.uniform_num AS uniformNum
                , p.birth
                , p.height
                , p.weight 
            FROM
                players p JOIN countries c 
                    ON p.country_id = c.id
            WHERE
                p.name LIKE ?
        EOM;

        
        // 入力された検索したい名前を取得
        $playerName = isset($_GET['playerName']) ? $_GET['playerName'] : '';
        $bindPlayerName = '%'.$playerName.'%';

        // チェックされたポジションを取得。pdo_in.phpと異なりチェックがない場合は配列が空。
        $positions = array();
        if (isset($_GET['positions'])) {
            $sql .= " AND p.position IN (%s)"; // チェックされている場合だけ、IN条件を追加
            $positions = $_GET['positions'];

            // 配列の要素の数だけIN内の?をカンマ区切りで生成し%s部分をprintf関数で置換
            $sql = sprintf($sql, substr(str_repeat(',?', count($positions)), 1));
        }

        $stmt = $dbh->prepare($sql);

        // LIKE条件のバインド
        $stmt->bindParam(1, $bindPlayerName, PDO::PARAM_STR); // pdo_like.phpとは異なり、名前付きプレースホルダではなく?マークでバインド
        
        // IN条件のバインド。ポジションが指定されていない場合は配列化が空なので実行されない。
        for($i = 0; $i < count($positions); $i++){
            $stmt->bindParam($i + 2, $positions[$i]); // 1つ目の?マークはLIKE条件にあるため、IN条件の?マークは2以降を指定
        }

        $stmt->execute();
        $rows = $stmt->fetchAll(PDO::FETCH_OBJ); // 全てのレコードを取得

        // 接続切断
        $dbh = null;

    } catch (PDOException $e) {
        print $e->getMessage() . "<br/>";
        die();
    }
?>

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
        <title>WHEREが動的に変わるサンプルシステム</title>
    </head>
    <body>
        <div class="container">

            <div class="h1 mb-3">WHEREが動的に変わるサンプルシステム</div>


            <div class="card mb-3">
                <div class="card-header bg-primary text-white">
                    検索
                </div>
                <div class="card-body">
                    <form action="">
                        <div class="row mb-3">
                            <div class="col-6">
                                <label for="playerName" class="form-label">選手の名前</label>
                                <input type="playerName" class="form-control" id="playerName" name="playerName" placeholder="" value="<?php print($playerName); ?>">
                            </div>

                        </div>

                        <div class="row mb-3">
                            <label class="form-check-label" for="">ポジション</label>
                            <div class="col-12">
                                <div class="form-check form-check-inline">
                                    <input class="form-check-input" type="checkbox" name="positions[]" value="GK" id="GK" <?php print(in_array('GK', $positions) ? 'checked' : '' ) ?>>
                                    <label class="form-check-label" for="GK">ゴールキーパー(GK)</label>
                                </div>
                                <div class="form-check form-check-inline">
                                    <input class="form-check-input" type="checkbox" name="positions[]" value="DF" id="DF" <?php print(in_array('DF', $positions) ? 'checked' : '' ) ?>>
                                    <label class="form-check-label" for="DF">ディフェンス(DF)</label>
                                </div>
                                <div class="form-check form-check-inline">
                                    <input class="form-check-input" type="checkbox" name="positions[]" value="MF" id="MF" <?php print(in_array('MF', $positions) ? 'checked' : '' ) ?>>
                                    <label class="form-check-label" for="MF">ミッドフィルダー(MF)</label>
                                </div>
                                <div class="form-check form-check-inline">
                                    <input class="form-check-input" type="checkbox" name="positions[]" value="FW" id="FW" <?php print(in_array('FW', $positions) ? 'checked' : '' ) ?>>
                                    <label class="form-check-label" for="FW">フォワード(FW)</label>
                                </div>
                            </div>
                        </div>
                    
	                    <button type="submit" class="btn btn-primary">検 索</button>
                    </form>
                </div>
            </div>



            <table class="table table-striped">
                <tr>
                    <th>ID</th>
                    <th>名前</th>
                    <th>国名</th>
                    <th>所属クラブ</th>
                    <th>ポジション</th>
                    <th>背番号</th>
                    <th>生年月日</th>
                    <th>身長</th>
                    <th>体重</th>
                </tr>

        <?php
            if($rows) {
                foreach($rows as $row){
        ?>
                <tr>
                    <td><?php print($row->playerId) ?></td>
                    <td><?php print($row->playerName) ?></td>
                    <td><?php print($row->countryName) ?></td>
                    <td><?php print($row->club) ?></td>
                    <td><?php print($row->position) ?></td>
                    <td><?php print($row->uniformNum) ?></td>
                    <td><?php print($row->birth) ?></td>
                    <td><?php print($row->height) ?>cm</td>
                    <td><?php print($row->weight) ?>kg</td>
                </tr>
        <?php
                }
            }
        ?>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>
    </body>
</html>

■解説

前回のINと違う点が、「チェックを入れなかった場合は条件なし」という仕様です。
ここが今回のポイントである「WHEREが動的に変わる」という部分に当たります。
具体的には36~42行目です。
positionsがGETで送信されていない場合にだけ、IN条件を追加します。

このように検索条件を入力していない場合は全件検索にしたい、あるいは複数の条件を組み合わせたい場合に
WHERE句を動的に変更する必要があります。