Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I pass a variable to a WHERE clause using mysqli?

I currently have a small search form with only two elements: a text input named search and a "select" box named param. The option selected within that box should be used as a parameter to choose which table column in my DB would be fetched.

Using mysql_ functions, something like this could be done:

$param  = $_POST['param' ];
$search = $_POST['search'];

$query = 'SELECT * FROM table WHERE $param LIKE "%$search%"';

But I can't get it to work with the mysqli_ syntax. I am trying to use prepared statements, but the best I've done so far is this:

$param  = $_POST['param' ];
$search = $_POST['search'];

if($param == 'first_name')
{
    if($prep = $link->prepare('SELECT * FROM table
                               WHERE first_name
                               LIKE CONCAT("%", ?, "%")'))
    {
        $prep->bind_param('s', $search);
        $prep->execute();
        $prep->bind_result($first_name, $last_name);

        while($prep->fetch())
            echo $first_name . ' ' . $last_name;

        $prep->close();
     }
     else
         echo 'Error while preparing statement.';
}
else if($param == 'last_name')
{
    ...
}

But just using a bunch of else ifs seems a lot repetitive and unproductive, specially if I have lots of columns to handle.

The first thing I tried was parameter binding - ... WHERE ? LIKE ... and $prep->bind_param('ss', $param, $search) -, but it didn't work (and I still don't know why).

Is there a way of doing it in a more intelligent way?

like image 842
Renato Avatar asked Jul 24 '13 22:07

Renato


3 Answers

If you are using the same SQL code for every param, just create a hash of possible params: (CGI param name => table column name)

$params = array(
    'first_name' => 'first_name',
    'last_name' => 'last_name',
);

It's much better from a security point of view as you are protected from SQL injection.

Then get the column name from the hash and put it into the query - and you'll get rid of the if-s:

$name = $params[$param];
$sql = "SELECT * FROM table
WHERE 
$name LIKE ?";

if($prep = $link->prepare($sql))
{
    $prep->bind_param('s', "%$search%");
    ...

As @Akam said, no need to CONCAT("%", ?, "%") in the query - it's better to bind the value with percents right ahead.

like image 177
user4035 Avatar answered Oct 19 '22 07:10

user4035


According to this example in the PHP manual

http://www.php.net/manual/en/mysqli-stmt.bind-param.php#108790

You seem to be best to add the '%' to the string variable you are binding - rather than in the query e.g. in your example:

if($prep = $link->prepare('SELECT * FROM table
                           WHERE first_name
                           LIKE ?'))
{
    $search='%'.$search.'%';
    $prep->bind_param('s', $search);
    $prep->execute();
    $prep->bind_result($first_name, $last_name);

    while($prep->fetch())
        echo $first_name . ' ' . $last_name;

    $prep->close();
 }

Haven't tested it but it seems like a sensible solution.

like image 26
Arthur Nicoll Avatar answered Oct 19 '22 09:10

Arthur Nicoll


You can't use placeholders for column names, so you'll have to concatenate the column names normally. However, instead of just escaping it with mysqli, because you have a limited set of columns I'd suggest a while list approach:

$allowed_params = array('first_name', 'last_name', etc);

$param  = $_POST['param' ];
$search = $_POST['search'];


if(!in_array($param, $allowed_params))
    die("Uh oh, the request seems to have an invalid param!");

if($prep = $link->prepare('SELECT * FROM table
                           WHERE ' . $param . '
                           LIKE ?'))
{
    $prep->bind_param('s', '%' . $search . '%');
    $prep->execute();
    $prep->bind_result($first_name, $last_name);

    while($prep->fetch())
        echo $first_name . ' ' . $last_name;

    $prep->close();
}
else
    echo 'Error while preparing statement.';

Also note the removal of the concat statement, and instead concatenating in PHP. This is important for prepared statements as once it gets to the server it's not actually combining them (thus the protection of prepared statements), so it won't work properly unless the wildcards are sent with the $search string.

like image 20
John V. Avatar answered Oct 19 '22 08:10

John V.