How to prepare and bind unpredictable parameters of a query in mysqli - php

I want to pull a list of data from a table based on the requests by a user.
1. $query = "SELECT * FROM users LIMIT 10";
2. $query = "SELECT * FROM users WHERE fname = ? LIMIT 10";
3. $query = "SELECT * FROM users WHERE fname = ? AND mname = ? LIMIT 10";
4. $query = "SELECT * FROM users WHERE fname = ? AND mname = ? AND lname = ? LIMIT 10";
If no parameter is provided, query (1)
If first name provided, query (2)
If first and middle name provided, query (3)
If all are provided, query (4)
It's hard for me to know which one the user will request.
How do I prepare, bind, execute, and fetch data of the chosen one from above?
UPDATE: more details.
<?php
$db = new mysqli("It's all OK");
$query = "SELECT * FROM users LIMIT 10";
$fname = (isset($_POST['fname']) AND !empty($_POST['fname'])) ? trim($_POST['fname']) : "";
$mname = (isset($_POST['mname']) AND !empty($_POST['mname'])) ? trim($_POST['mname']) : "";
$lname = (isset($_POST['lname']) AND !empty($_POST['lname'])) ? trim($_POST['lname']) : "";
if(!empty($fname) AND empty($mname) AND empty($lname)){
$query .= " WHERE fname = ? LIMIT 10";
}elseif(!empty($fname) AND !empty($mname) AND empty($lname)){
$query .= " WHERE fname = ? AND mname = ? LIMIT 10";
}elseif(!empty($fname) AND !empty($mname) AND !empty($lname)){
$query .= " WHERE fname = ? AND mname = ? AND lname = ? LIMIT 10";
}
?>
Given all the details, query is built but it's hard to predict what the user will request.
I have done:
$stmt = $db->prepare($query);
Now I have a problem binding the unpredictable parameters.
Please help.

I guess you have variables somewhere like fname, mname, and lname. Put them in an array like
$options = ['fname' => $fname, 'mname' => $mname, 'lname'=> $lname];
$defaults = ['fname' => '', 'mname' => '', 'lname'=> ''];
$options = array_merge($defaults, $options);
$options = array_diff($options, []);
$query = "SELECT * FROM users";
foreach ($options as $key => $value){
$query .= " AND $key = ?"
}
$query .= ' LIMIT 10';

There is a brilliant solution
$fname = (!empty($_POST['fname'])) ? trim($_POST['fname']) : NULL;
$mname = (!empty($_POST['mname'])) ? trim($_POST['mname']) : NULL;
$query = "SELECT * FROM users WHERE
(? is null OR fname = ?)
AND (? is null OR mname = ?)
AND so on
LIMIT 10";
A variable will be used in the query only if its value is not null.
this way you will need only one query and one set of parameters for any number of parameter combinations.
$stmt = $db->prepare($query);
$stmt->bind_param('ss', $fname, $fname, $mname, $mname);
$stmt->execute();
(You need to bind every variable twice though).
But this approach will leave you with only one query and with straight call to bind_param() with constant number of variables.
But if you still want to bind unknown number of variables, here is the solution.
Your own solution won't work if only lname provided or fname and lname but no mname.
Sometimes I think that sharing knowledge on SO is a biggest waste of time.

Thanks all for your effort to answer. I really appreciated it.
After several attempts, I manage it to work.
$stmt->prepare($query);
if(!empty($fname) AND empty($mname) AND empty($lname)){
$stmt->bind_param("s", $fname);
}elseif(!empty($fname) AND !empty($mname) AND empty($lname)){
$stmt->bind_param("ss", $fname, $mname);
}elseif(!empty($fname) AND !empty($mname) AND !empty($lname)){
$stmt->bind_param("sss",$fname,$mname,$lname);
}
$stmt->execute()
and bind result, fetch it that's all.
It might not be the best answer, but it does work.
Again, thanks all.

Related

Add additional filter in PHP SQL search funtion

The following is the PHP code I am using for a simple search feature in my website.
The search simply shows refults if it matches the SQL column "tags".
I would like to add one more filter in the SQL query.
I want to filter the search results based on city.
The city data is already in the SQL, but I dont know how to add it here without breaking the properly working search funtion.
I tried $data_sql .= " AND city='newyork' "; after the 8th line, but it didnt work.
$name=str_replace(' ', '%', $_POST['query']);
$newsearch = "%$name%";
$base_sql = "SELECT %s FROM posts WHERE tags LIKE ?";
$count_sql = sprintf($base_sql, "count(*)");
$stmt = $connect->prepare($count_sql);
$stmt->execute([$newsearch]);
$total_data = $stmt->fetchColumn();
$data_sql = $count_sql = sprintf($base_sql, "*")." LIMIT ?,?";
$stmt = $connect->prepare($data_sql);
$stmt->execute([$newsearch, $start, $limit]);
$result = $stmt->fetchAll();
So your additional filter must be before LIMIT ?, ?
if you try adding it after the 8th line the query will look like this:
SELECT * FROM posts WHERE tags LIKE 'search' LIMIT 0, 100 AND city='newyork'
so what can you do:
$data_sql = sprintf($base_sql, "*");//we will add the limit before preparation
//don't know why do you need that $count_sql here
$data_sql .= " AND city='newyork' ";
//IF you need some GROUP BY do it here
//If you need some ORDER BY do it here
$data_sql .= " LIMIT ?, ?";
$stmt = $connect->prepare($data_sql);
$stmt->execute([$newsearch, $start, $limit]);
$result = $stmt->fetchAll();
The line $data_sql .= " AND city='newyork' "; won't work as it will add the string after the LIMIT which is not a valid sql query.
You should instead edit the line with the base_sql like this:
$base_sql = "SELECT %s FROM posts WHERE tags LIKE ? AND city='newyork'";
And of course if 'newyork' needs to be a variable you can do thr same thing like you did for the tags
First, let's add the new criteria:
$base_sql = "SELECT %s FROM posts WHERE tags LIKE ? and city = ?";
Then make sure that you pass the city as a parameter
$stmt->execute([$newsearch, 'newyork', $start, $limit]);

How do I add a string at the end of user input in SQL/PHP

I have a SQL query that is based on user input.
However, in the table, theres a "-1" at the end of every word that you search for.
For example if you want to get the sql result of car, it's actually named car-1 in the database, but the user should only be able to search for car.
This is how its setup:
$sql = "SELECT * FROM that WHERE this = ?";
$stmt = $conn->prepare($sql);
$search_query = $_POST['this'];
$stmt->bind_param('s', $search_query);
$stmt->execute();
$result = $stmt->get_result();
What I want, is that the select query should be like:
$sql = "SELECT * FROM that WHERE this = ? + '-1'";
But ^^ doesn't work.
$sql = "SELECT * FROM test WHERE NAME='car' & -1";
test = that
NAME= table name
'car' = this
Why don't you just concat -1 to search_query :
$sql = "SELECT * FROM that WHERE this = ?";
$stmt = $conn->prepare($sql);
$search_query = $_POST['this'];
$stmt->bind_param('s', $search_query.'-1');
$stmt->execute();
$result = $stmt->get_result();
Using MySQL:
$sql = "SELECT * FROM that WHERE this = CONCAT(?, '-1')";
Using PHP:
$stmt->bind_param('s', $search_query . "-1");

Pagination 2nd page not displaying

Pagination works fine when I don't use the WHERE statement in my SELECT statement. For some reason as soon as I add additional requests in the SELECT statement, only the 1st pagination page works. So it seems like the variable data is lost after the first page is displayed. Below is some of the code:-
<?php
include 'database.php';
include 'paginator.php';
$pdo = Database::connect();
$paginator = new Paginator();
$sql = "SELECT count(*) FROM customer_crm ";
$paginator->paginate($pdo->query($sql)->fetchColumn());
$query = $_GET["query"];
if (isset($query)) {
($_GET['query'])?('%'.$_GET['query'].'%'):'%';
$sql = "SELECT * FROM customer_crm WHERE firstname LIKE :query OR email LIKE :query OR telephone LIKE :query ";
}
else {
$start = (($paginator->getCurrentPage()-1)*$paginator->itemsPerPage);
$length = ($paginator->itemsPerPage);
//$sql = "SELECT * FROM customer_crm WHERE customer_group_id = $input OR date_followup= CURDATE() ORDER BY customer_group_id DESC limit $start, $length ";
$sql = "SELECT * FROM customer_crm ORDER BY date_followup DESC limit $start, $length ";
//$sql = "SELECT * FROM customer_crm WHERE customer_group_id = $input ORDER BY date_followup DESC limit $start, $length ";
}
$sth = $pdo->prepare($sql);
$sth->bindParam(':start',$start,PDO::PARAM_INT);
$sth->bindParam(':length',$length,PDO::PARAM_INT);
$sth->bindParam(':query',$query,PDO::PARAM_STR);
$sth->execute();
foreach ($sth->fetchAll(PDO::FETCH_ASSOC) as $row) {
Without knowing which Paginator are we talking about, I could only advise you to do something like
include 'database.php';
include 'paginator.php';
$pdo = Database::connect();
$paginator = new Paginator();
$query = (isset($_GET["query"]) && strlen($_GET["query"])>1)? '%'.$_GET["query"].'%':'%';
$countsql = "SELECT * FROM customer_crm WHERE firstname LIKE :query OR email LIKE :query OR telephone LIKE :query ";
$sthcount = $pdo->prepare($countsql);
$sthcount->bindParam(':query',$query,PDO::PARAM_STR);
$sthcount->execute();
$count=$sthcount->fetchColumn();
$paginator->paginate($count);
$start = (($paginator->getCurrentPage()-1)*$paginator->itemsPerPage);
$length = ($paginator->itemsPerPage);
$sql = $countsql . ' ORDER BY date_followup DESC limit :start, :length ';
$sth = $pdo->prepare($sql);
$sth->bindParam(':start',$start,PDO::PARAM_INT);
$sth->bindParam(':length',$length,PDO::PARAM_INT);
$sth->bindParam(':query',$query,PDO::PARAM_STR);
$sth->execute();
See, you where making two mistakes here:
getting your count value without considering the query. You should set the value of $query regardless of the existance of $_GET['query'], and use it in your count query as well as your results query.
binding parameters whose placeholders and values do not exist in the query you're executing. Make sure your results query contains :query, :start and :length or you will be binding more parameters than the query has.
You should also have wrapped your statements in try/catch blocks so you could debug what was happening.
try {
$sth = $pdo->prepare($sql);
$sth->bindParam(':start',$start,PDO::PARAM_INT);
$sth->bindParam(':length',$length,PDO::PARAM_INT);
$sth->bindParam(':query',$query,PDO::PARAM_STR);
$sth->execute();
} catch(\PDOException $e) {
die('Error in query: '. $e->getMessage());
}
That way you would have known that the query was failing because of
Invalid parameter number: parameter was not defined
NOTE I have no clue about how your paginator will know about the current page, nor can I see where are you setting the itemsPerPage value.

Return a default value if no records are found

I have this PHP code that selects one simple value:
$name = $mysqli->query('SELECT name FROM users WHERE id = ' . $id . ' LIMIT 1')->fetch_object()->name;
Now if the result is 0, it throws an error.
I want instead to return a default value like "No name" if there are no results.
Any suggestions?
Don't fetch the name directly, instead check, if there is data in it first. Something like this should work:
$result = $mysqli->query('SELECT name FROM users WHERE id = ' . $id . ' LIMIT 1')->fetch_object();
$name = (!$result) ? 'No name' : $result->name;
Additional, as #RiggsFolly mentioned, it is suggested to use prepared statements, to reduce SQL possibilities:
$stmt = $mysqli->prepare("SELECT name FROM users WHERE id = ? LIMIT 1");
$stmt->bind_param('i',$id);
$stmt->execute();
$result = $stmt->get_result()->fetch_object();
This can be done in pure SQL so that you wouldn't need any extra line of PHP code :
$name = $mysqli->query('SELECT IFNULL((SELECT name FROM users WHERE id = ' . $id . ' LIMIT 1), "No Name") name')->fetch_object()->name;
Try this, with prepared statement:
$result = $mysqli->prepare('SELECT name FROM users WHERE id = ? LIMIT 1');
$result->bind_param("i", $id);
$result->execute();
$result->bind_result($col1);
$result->fetch();
$name = (!$col1) ? 'No name' : $col1;
echo $name;
$result->close();

MySQL query based on user input

I have a DB table. I want to make a text input where the user can input the "uid" and the query will return the row associated with that uid.
So let's say I have something like this:
$query = "SELECT name,age FROM people WHERE uid = '2' LIMIT 0,1";
$result = mysql_query($query);
$res = mysql_fetch_assoc($result);
echo $res["age"];
how would I modify that query to something like..
SELECT name, age
FROM people
WHERE uid = $_POST['blahblah'] LIMIT 0,1
Thanks in advance for your help!
In reality...
// Read input from $_POST
$uid = (isset($_POST['uid']) ? $_POST['uid'] : '');
// Build query. Properly escape input data.
$query =
"SELECT name,age " .
"FROM people " .
"WHERE uid = '" . mysql_real_escape_string($uid) . "' " .
"LIMIT 0,1";
Its advisable to escape characters in the variable for security reasons. Take a look at this document for some of the reasons:
http://en.wikipedia.org/wiki/SQL_injection
To save from SQL injection attack, use:
$search_query = mysql_real_escape_string($_POST['blahblah']);
$query = "SELECT name, age FROM people WHERE uid = '".$search_query."' LIMIT 0 , 1";
There are so many ways to do the same
But first escape it and store it in one variable
$blahblah = mysql_real_escape_string($_POST['blahblah']);
And then There are
First:
As #Mett Lo mentioned:
$query = "SELECT name,age FROM people WHERE uid = '" . $blahblah . "' LIMIT 0,1";
Second:
$query = "SELECT name,age FROM people WHERE uid = '{$blahblah}' LIMIT 0,1";
Third:
$query = "SELECT name,age FROM people WHERE uid = '$blahblah' LIMIT 0,1";
and if blahblah is an int value in db table then Fourth:
$query = "SELECT name,age FROM people WHERE uid = $blahblah LIMIT 0,1";
You may use the sprintf function to create the query.
$query = sprintf("SELECT name,age FROM people WHERE uid = '%s' LIMIT 0,1",
$_POST['blahblah'] );
The rest will be the same. It is highly recommended that you escape the $_POST data before running the query to prevent SQL attacks. You may re phrase the query as follows.
$query = sprintf("SELECT name,age FROM people WHERE uid = '%s' LIMIT 0,1",
mysql_escape_string($_POST['blahblah']) );

Categories