5

我是 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()));
    }
}

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

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

...它将返回此错误:“SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens”

那么,如果我想为可选参数保留准备好的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);
    }
}
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

有一个不错的小功能可以提供帮助:最小的查询生成器。使代码看起来像这样不需要框架或 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 回答