5

私は PDO の初心者で、現在、検索結果を返す API 呼び出しを開発しています。検索クエリに 2 つのオプション パラメータがある場合、prepare ステートメントを設定するにはどうすればよいですか?

$app->get('/get/search', function () {
    $sql = 'SELECT * FROM user WHERE name LIKE :name AND city = :city AND gender = :gender';
    try {
        $stmt = cnn()->prepare($sql);
        $stmt->bindParam(':name', '%'.$_GET['name'].'%', PDO::PARAM_STR);
        $stmt->bindParam(':city', '%'.$_GET['city'].'%', PDO::PARAM_STR);
        $stmt->bindParam(':gender', $_GET['gender'], PDO::PARAM_INT);
        $stmt->execute();
        if($data = $stmt->fetchAll()) {
            echo json_encode($data);
        } else {
            echo json_encode(array('error' => 'no records found');
        }
    } catch(PDOException $e) {
        echo json_encode(array('error' => $e->getMessage()));
    }
}

ここでの問題は、$_GET['city']$_GET['gender'] の両方がオプションであることです。上記のコードを実行しようとすると、空の変数は列の空の値にも一致する必要があると想定されます。一方、次のようなことをすると:

if($_GET['gender']) $stmt->bindParam(':gender', $_GET['gender'], PDO::PARAM_INT);

...次のエラーが返されます: "SQLSTATE[HY093]: 無効なパラメーター番号: バインドされた変数の数がトークンの数と一致しません"

では、オプションのパラメーターに対して準備済みの sql ステートメントを保持したい場合の解決策は何ですか? ありがとう!

アップデート

これは、受け入れられた回答といくつかのコメント ( decezeおよびbill-karwin による)に基づくソリューションです。

if($_GET['name']) $where[] = 'name LIKE :name';
if($_GET['city']) $where[] = 'city LIKE :city';
if(isset($_GET['gender'])) $where[] = 'gender = :gender';
if(count($where)) {
    $sql = 'SELECT * FROM user WHERE '.implode(' AND ',$where);
    $stmt = cnn()->prepare($sql);
    $name = '%'.$_GET['name'].'%';
    if($_GET['name']) $stmt->bindValue(':name', '%'.$_GET['name'].'%', PDO::PARAM_STR);
    $city = '%'.$_GET['city'].'%';
    if($_GET['city']) $stmt->bindParam(':city', $city, PDO::PARAM_STR);
    if(isset($_GET['gender'])) $stmt->bindParam(':gender', $_GET['gender'], PDO::PARAM_BOOL);
    $stmt->execute();
    if($data = $stmt->fetchAll()) {
        echo json_encode($data);
    }
}
4

2 に答える 2

16

いくつかの古き良き動的 SQL クエリを組み合わせて...

$sql = sprintf('SELECT * FROM user WHERE name LIKE :name %s %s',
               !empty($_GET['city'])   ? 'AND city   = :city'   : null,
               !empty($_GET['gender']) ? 'AND gender = :gender' : null);

...

if (!empty($_GET['city'])) {
    $stmt->bindParam(':city', '%'.$_GET['city'].'%', PDO::PARAM_STR);
}

...

おそらくこれをより適切に表現して、ヘルパー関数などでラップすることもできますが、これが基本的な考え方です。

于 2013-05-31T20:55:31.150 に答える
2

役立つ小さな関数があります: tinyest query builder . コードを次のようにするために必要なフレームワークや ORM はありません。

public function updateUser(int $id, string $email = '', string $password = '', string $name = '') {
    $sql = \App\Utils\build_query([
        [               'UPDATE "users"'],
        [$email         ,'SET', 'email=:email'],
        [$password      ,',',   'password=:password'],
        [$name          ,',',   'name=:name'],
        [               'WHERE "id"=:id']
    ]);

    $stmt = $this->db->prepare($sql);
    $stmt->bindValue(':id', $id, \PDO::PARAM_INT);
    // Optional bindings.
    $email &&       $stmt->bindValue(':email', $email, \PDO::PARAM_STR);
    $password &&    $stmt->bindValue(':password', $password, \PDO::PARAM_STR);
    $name &&        $stmt->bindValue(':name', $name, \PDO::PARAM_STR);

    $stmt->execute();
}

もちろん、オプションのコンポーネントをサポートして、クエリコンポーネントがいかにきちんと作成されているかに注目してください。&&バインディングによるエクスプションは、このパラメーターが指定されているかどうかを単純にチェックし、指定されている場合は適切なパラメーターがbindValue呼び出されます。

于 2016-09-14T16:09:40.970 に答える