2013-05-31 70 views
3

我是一種新的與PDO和當前開發的API調用返回搜索結果。如果搜索查詢有兩個可選參數,如何設置一個準備語句?PDO準備聲明與可選參數

$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 [ '城市']$ _GET [ '性別']是可選的。如果我嘗試運行上面的代碼,它會假定任何空變量都應該匹配列中的空值;在另一方面,如果我做這樣的事:

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); 
    } 
} 
+0

性別是'int'嗎? –

+0

實際上它是一個很小的int(0或1)。但這不是問題。問題是性別和城市(或任何其他未來參數)在查詢字符串中應該是可選的。準備好的聲明是否可行? – andufo

+0

@LuigiSiri我現在使用的PDO :: PARAM_BOOL,因爲它只能是1或0. – andufo

回答

9

一些很好的舊動態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); 
} 

... 

你可以表達這個更好的東西,並把它包裝在輔助函數等等等,但這是基本的想法。

+4

+1另一種方法是建立一個搜索項的數組,然後'implode(「AND」)'數組。 –

+0

不錯的主意。我會用這種方法。謝謝! – andufo

+0

我已根據您的答案更新了原始帖子並提供瞭解決方案。謝謝! – andufo

0

有一個很好的小功能可以幫助: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