Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PDO prepared statement with optional parameters

I'm kind of new with PDO and currently developing the API call that returns search results. How do I set a prepare statement if there are 2 optional parameters for the search query?

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

The issue here, is that both $_GET['city'] and $_GET['gender'] are optional. If I try to run the code above, it will asume that any empty variable should match an empty value in the column as well; in the other hand, if I do something like this:

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

...it will return this error: "SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens"

So, what's the solution if I want to keep the prepared sql statement for optional parameters? Thanks!

Update

This is the solution based on the accepted answer and some comments (by deceze and 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);
    }
}
like image 477
Andres SK Avatar asked May 31 '13 20:05

Andres SK


1 Answers

Some good old dynamic SQL query cobbling-together...

$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);
}

...

You can probably express this nicer and wrap it in helper functions etc. etc, but this is the basic idea.

like image 150
deceze Avatar answered Oct 04 '22 21:10

deceze