PDO准备语句带有可选参数

And*_* SK 5 php mysql pdo prepared-statement

我是PDO的新手,目前正在开发返回搜索结果的API调用.如果搜索查询有2个可选参数,如何设置准备语句?

$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()));
    }
}
Run Code Online (Sandbox Code Playgroud)

这里的问题是$ _GET ['city']$ _GET ['gender']都是可选的.如果我尝试运行上面的代码,它将假设任何空变量也应匹配列中的空值; 另一方面,如果我做这样的事情:

if($_GET['gender']) $stmt->bindParam(':gender', $_GET['gender'], PDO::PARAM_INT);
Run Code Online (Sandbox Code Playgroud)

...它将返回此错误:"SQLSTATE [HY093]:参数号无效:绑定变量数与令牌数不匹配"

那么,如果我想为可选参数保留准备好的sql语句,那么解决方案是什么?谢谢!

更新

这是基于接受的答案和一些评论(由decezebill-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);
    }
}
Run Code Online (Sandbox Code Playgroud)

dec*_*eze 12

一些好的旧动态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);
}

...
Run Code Online (Sandbox Code Playgroud)

你可以表达这个更好,并将它包装在辅助函数等等,但这是基本的想法.

  • +1另一种方法是构建一个搜索项数组,然后`implode("AND")`数组. (4认同)