PDO准备语句带有可选参数

时间:2013-05-31 20:49:21

标签: 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()));
    }
}

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

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

...它将返回此错误:“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);
    }
}

2 个答案:

答案 0 :(得分:11)

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

...

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

答案 1 :(得分:1)

有一个很好的小功能可以提供帮助:tiniest 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