我是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语句,那么解决方案是什么?谢谢!
更新
这是基于接受的答案和一些评论(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);
}
}
答案 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
。