Bind parameters with PDO in PHP - php

I am using PDO in php. But when my query have any keyword like " ' " means hyphen it breaks and through an error.
I ready through on internet and find solution to bind parameters with query and it works fine.
But the issue is i am building the query in loop and i am not able to bind parameters within loop.
Here is code in which i am splitting the array with space and run query on every keyword.
The first 3 words will have only like query and more then 3 words i am using loop to concatenate the all array elements and same with more then 6 words i am using MATCH query.
Is there any way to escape that hyphen or how can we bind parameters using loop in my case?
$keyword = ($_POST['keyword']);
$keyword_array = split(' ',$keyword);
/* Query For first Three Words */
if(count($keyword_array)<=3){
$sql = "SELECT * FROM faq WHERE question LIKE '%$keyword%' limit 14";
}
/* Query through all array when words are greater then 3 */
if(count($keyword_array)< 6){
$sql = "SELECT * FROM faq WHERE question ";
for($i = 0 ; $i<count($keyword_array); $i++){
if($i==0){
$sql.=" LIKE '%$keyword_array[$i]%'";
}else{
$sql.=" or question LIKE '%$keyword_array[$i]%' ";
}
}
$sql .= " ORDER BY question ASC LIMIT 0, 8";
}
/* Appl FULL TEXT in natual language mode once we have enough phrase */
else if(count($keyword_array)>=6){
$sql = "SELECT * FROM faq WHERE ";
for($i = 0 ; $i<count($keyword_array); $i++){
if($i==0){
$sql.=" MATCH (answer) AGAINST ('$keyword_array[$i]' in natural language mode) ";
}else{
$sql.=" or MATCH(answer) AGAINST('$keyword_array[$i]' in natural language mode) ";
}
}
$sql .= " limit 0,5";
}
$execute_faq_query = $conn->query($sql);
$execute_faq_query->setFetchMode(PDO::FETCH_ASSOC);
while ($list = $execute_faq_query->fetch()){
}

When building a dynamic query you need to separate those parts of the query that are static from those that are dynamic.
You can see that the following code is static.
"SELECT * FROM faq ";
The rest of the code is dynamic. When filtering records the WHERE clause is used and the AND & OR operators are used to filter records based on more than one condition. The AND operator displays a record if both the first condition AND the second condition are true. The OR operator displays a record if either the first condition OR the second condition is true. so for the first condition WHERE is used but after that AND or OR must be used(using OR in your example)
// Static code
sql = "SELECT * FROM `faq`"
// Set initial condition to WHERE
clause = "WHERE";
if( !empty( filter )){
Add clause to sql
Add condition to sql
change clause to OR or AND as required
}
Repeat for each filter
Note the filter is not changed until a filter is applied and remains changed once changed.
The remaining static code, if any,is added after all the filters have been handled.
I have used Switch Case to apply filters and unnamed parameters ?.
Use "lazy" binding when possible - passing data into execute will dramatically shorten your code. See PDO info.
//Test $POST[] remove after testing
$_POST['keyword'] ="one two three four five six";
$keyword = ($_POST['keyword']);
$keyword_array = split(' ',$keyword);
$words = count($keyword_array);
echo $words;
//You need an array to store parameters
$paramArray =array();
//Initial clause
$clause = "WHERE";
//Start with a basic stub
$sql = "SELECT * FROM faq ";
switch (true) {
case $words <= 3:
$sql .= " $clause question LIKE ?";
$keyword = "%$keyword%";
array_push($paramArray,$keyword);
$limit = " LIMIT 14";
break;
case $words < 6:
for($i = 0 ; $i<count($keyword_array); $i++){
$sql .= " $clause question LIKE ?";
$keyword = "%$keyword_array[$i]%";
array_push($paramArray,$keyword);
$clause = "OR";
$limit = " ORDER BY question ASC LIMIT 0, 8";
}
break;
case $words >=6:
$clause = "";
for($i = 0 ; $i<count($keyword_array); $i++){
$sql.=" $clause MATCH (answer) AGAINST (? in natural language mode) ";
array_push($paramArray,$keyword_array[$i]);
$clause = "OR";
$limit = " limit 0,5";
}
break;
}
//echo query and parameter array remove after testing
echo $sql;
echo "<br>";
print_r($paramArray);
//Prepare and execute query
$execute_faq_query = $conn->prepare($sql);
$execute_faq_query->execute($paramArray);
$execute_faq_query->setFetchMode(PDO::FETCH_ASSOC);
while ($list = $execute_faq_query->fetch()){
}

Related

How to add an optional condition to a prepared statement? [duplicate]

I need to change this query to use a prepared statement. Is it possible?
The query:
$sql = "SELECT id, title, content, priority, date, delivery FROM tasks " . $op . " " . $title . " " . $content . " " . $priority . " " . $date . " " . $delivery . " ORDER BY " . $orderField . " " . $order . " " . $pagination . "";
Before the query, there's code to check the POST variables and change the content of variables in the query.
//For $op makes an INNER JOIN with or without IN clause depending on the content of a $_POST variable
$op = "INNER JOIN ... WHERE opID IN ('"$.opID."')";
//Or
$op = "INNER JOIN ... ";
//For $title (depends of $op):
$title = "WHERE title LIKE'%".$_POST["title"]."%'";
//Or
$title = "AND title LIKE'%".$_POST["title"]."%'";
//For $content:
$content = "AND content LIKE '%".$_POST["content"]."%'";
//For $priority just a switch:
$priority = "AND priority = DEPENDING_CASE";
//For $date and $delivery another switch
$d = date("Y-m-d", strtotime($_POST["date"]));
$date = "AND date >= '$d' 00:00:00 AND date <= '$d' 23:59:59";
//Or $date = "AND date >= '$d' 00:00:00";
//Or $date = "AND date <= '$d' 23:59:59";
//For $orderField
$orderField = $_POST["column"];
//For $order
$order= $_POST["order"];
//For $pagination
$pagination = "LIMIT ".$offset.",". $recordsPerPage;
How I could do this query using prepared statement?
The query could be more static but this means to make different prepared statements and execute it depending of $_POST checks.
It depends on many variables because this query show results in a table that contains search fields and column to order.
A full example of query would be like this (depending of $_POST checks):
SELECT id, title, content, priority, date, delivery FROM tasks INNER JOIN op ON task.op = op.opId WHERE op IN (4851,8965,78562) AND title LIKE '%PHT%' AND content LIKE '%%' AND priority = '2' ORDER BY date DESC LIMIT 0, 10
An excellent question. And thank you for moving to prepared statements. It seems that after all those years of struggle, the idea finally is starting to take over.
Disclaimer: there will be links to my own site because I am helping people with PHP for 20+ years and got an obsession with writing articles about most common issues.
Yes, it's perfectly possible. Check out my article, How to create a search filter for mysqli for the fully functional example.
For the WHERE part, all you need is to create two separate arrays - one containing query conditions with placeholders and one containing actual values for these placeholders, i.e:
WHERE clause
$conditions = [];
$parameters = [];
if (!empty($_POST["content"])) {
$conditions[] = 'content LIKE ?';
$parameters[] = '%'.$_POST['content ']."%";
}
and so on, for all search conditions.
Then you could implode all the conditions using AND string as a glue, and get a first-class WHERE clause:
if ($conditions)
{
$where .= " WHERE ".implode(" AND ", $conditions);
}
The routine is the same for all search conditions, but it will be a bit different for the IN() clause.
IN() clause
is a bit different as you will need more placeholders and more values to be added:
if (!empty($_POST["opID"])) {
$in = str_repeat('?,', count($array) - 1) . '?';
$conditions[] = "opID IN ($in)";
$parameters = array_merge($parameters, $_POST["opID"]);
}
this code will add as many ? placeholders to the IN() clause as many elements in the $_POST["opID"] and will add all those values to the $parameters array. The explanation can be found in the adjacent article in the same section on my site.
After you are done with WHERE clause, you can move to the rest of your query
ORDER BY clause
You cannot parameterize the order by clause, because field names and SQL keywords cannot be represented by a placeholder. And to tackle with this problem I beg you to use a whitelisting function I wrote for this exact purpose. With it you can make your ORDER BY clause 100% safe but perfectly flexible. All you need is to predefine an array with field names allowed in the order by clause:
$sortColumns = ["title","content","priority"]; // add your own
and then get safe values using this handy function:
$orderField = white_list($_POST["column"], $sortColumns, "Invalid column name");
$order = white_list($_POST["order"], ["ASC","DESC"], "Invalid ORDER BY direction");
this is a smart function, that covers three different scenarios
in case no values were provided (i.e. $_POST["column"] is empty) the first value from the white list will be used, so it serves as a default value
in case a correct value provided, it will be used in the query
in case an incorrect value is provided, then an error will be thrown.
LIMIT clause
LIMIT values are perfectly parameterized so you can just add them to the $parameters array:
$limit = "LIMIT ?, ?";
$parameters[] = $offset;
$parameters[] = $recordsPerPage;
The final assembly
In the end, your query will be something like this
$sql = "SELECT id, title, content, priority, date, delivery
FROM tasks INNER JOIN ... $where ORDER BY `$orderField` $order $limit";
And it can be executed using the following code
$stmt = $mysqli->prepare($sql);
$stmt->bind_param(str_repeat("s", count($parameters)), ...$parameters);
$stmt->execute();
$data = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
where $data is a conventional array contains all the rows returned by the query.

How to make a fully dynamic prepared statement using mysqli API?

I need to change this query to use a prepared statement. Is it possible?
The query:
$sql = "SELECT id, title, content, priority, date, delivery FROM tasks " . $op . " " . $title . " " . $content . " " . $priority . " " . $date . " " . $delivery . " ORDER BY " . $orderField . " " . $order . " " . $pagination . "";
Before the query, there's code to check the POST variables and change the content of variables in the query.
//For $op makes an INNER JOIN with or without IN clause depending on the content of a $_POST variable
$op = "INNER JOIN ... WHERE opID IN ('"$.opID."')";
//Or
$op = "INNER JOIN ... ";
//For $title (depends of $op):
$title = "WHERE title LIKE'%".$_POST["title"]."%'";
//Or
$title = "AND title LIKE'%".$_POST["title"]."%'";
//For $content:
$content = "AND content LIKE '%".$_POST["content"]."%'";
//For $priority just a switch:
$priority = "AND priority = DEPENDING_CASE";
//For $date and $delivery another switch
$d = date("Y-m-d", strtotime($_POST["date"]));
$date = "AND date >= '$d' 00:00:00 AND date <= '$d' 23:59:59";
//Or $date = "AND date >= '$d' 00:00:00";
//Or $date = "AND date <= '$d' 23:59:59";
//For $orderField
$orderField = $_POST["column"];
//For $order
$order= $_POST["order"];
//For $pagination
$pagination = "LIMIT ".$offset.",". $recordsPerPage;
How I could do this query using prepared statement?
The query could be more static but this means to make different prepared statements and execute it depending of $_POST checks.
It depends on many variables because this query show results in a table that contains search fields and column to order.
A full example of query would be like this (depending of $_POST checks):
SELECT id, title, content, priority, date, delivery FROM tasks INNER JOIN op ON task.op = op.opId WHERE op IN (4851,8965,78562) AND title LIKE '%PHT%' AND content LIKE '%%' AND priority = '2' ORDER BY date DESC LIMIT 0, 10
An excellent question. And thank you for moving to prepared statements. It seems that after all those years of struggle, the idea finally is starting to take over.
Disclaimer: there will be links to my own site because I am helping people with PHP for 20+ years and got an obsession with writing articles about most common issues.
Yes, it's perfectly possible. Check out my article, How to create a search filter for mysqli for the fully functional example.
For the WHERE part, all you need is to create two separate arrays - one containing query conditions with placeholders and one containing actual values for these placeholders, i.e:
WHERE clause
$conditions = [];
$parameters = [];
if (!empty($_POST["content"])) {
$conditions[] = 'content LIKE ?';
$parameters[] = '%'.$_POST['content ']."%";
}
and so on, for all search conditions.
Then you could implode all the conditions using AND string as a glue, and get a first-class WHERE clause:
if ($conditions)
{
$where .= " WHERE ".implode(" AND ", $conditions);
}
The routine is the same for all search conditions, but it will be a bit different for the IN() clause.
IN() clause
is a bit different as you will need more placeholders and more values to be added:
if (!empty($_POST["opID"])) {
$in = str_repeat('?,', count($array) - 1) . '?';
$conditions[] = "opID IN ($in)";
$parameters = array_merge($parameters, $_POST["opID"]);
}
this code will add as many ? placeholders to the IN() clause as many elements in the $_POST["opID"] and will add all those values to the $parameters array. The explanation can be found in the adjacent article in the same section on my site.
After you are done with WHERE clause, you can move to the rest of your query
ORDER BY clause
You cannot parameterize the order by clause, because field names and SQL keywords cannot be represented by a placeholder. And to tackle with this problem I beg you to use a whitelisting function I wrote for this exact purpose. With it you can make your ORDER BY clause 100% safe but perfectly flexible. All you need is to predefine an array with field names allowed in the order by clause:
$sortColumns = ["title","content","priority"]; // add your own
and then get safe values using this handy function:
$orderField = white_list($_POST["column"], $sortColumns, "Invalid column name");
$order = white_list($_POST["order"], ["ASC","DESC"], "Invalid ORDER BY direction");
this is a smart function, that covers three different scenarios
in case no values were provided (i.e. $_POST["column"] is empty) the first value from the white list will be used, so it serves as a default value
in case a correct value provided, it will be used in the query
in case an incorrect value is provided, then an error will be thrown.
LIMIT clause
LIMIT values are perfectly parameterized so you can just add them to the $parameters array:
$limit = "LIMIT ?, ?";
$parameters[] = $offset;
$parameters[] = $recordsPerPage;
The final assembly
In the end, your query will be something like this
$sql = "SELECT id, title, content, priority, date, delivery
FROM tasks INNER JOIN ... $where ORDER BY `$orderField` $order $limit";
And it can be executed using the following code
$stmt = $mysqli->prepare($sql);
$stmt->bind_param(str_repeat("s", count($parameters)), ...$parameters);
$stmt->execute();
$data = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
where $data is a conventional array contains all the rows returned by the query.

How to use PDO prepared statements with IN clause? [duplicate]

This question already has answers here:
Can I bind an array to an IN() condition in a PDO query?
(23 answers)
Closed 1 year ago.
I stored some data in a field inside MySQL in this format: 1,5,9,4
I named this field related. Now I want to use this field inside an IN clause with PDO. I stored that field contents in $related variabe. This is my next codes:
$sql = "SELECT id,title,pic1 FROM tbl_products WHERE id IN (?) LIMIT 4";
$q = $db->prepare($sql);
$q->execute(array($related));
echo $q->rowCount();
But after executing this code, I can fetch only one record whereas I have to fetch 4 records (1,5,9,4). What did I do wrong?
using named place holders
$values = array(":val1"=>"value1", ":val2"=>"value2", ":val2"=>"value3");
$statement = 'SELECT * FROM <table> WHERE `column` in(:'.implode(', :',array_keys($values)).')';
using ??
$values = array("value1", "value2", "value3");
$statement = 'SELECT * FROM <table> WHERE `column` in('.trim(str_repeat(', ?', count($values)), ', ').')';
You need as many ? placeholders as your "IN" values.
So:
$related = array(1,2,3); // your "IN" values
$sql = "SELECT id,title,pic1 FROM tbl_products WHERE id IN (";
$questionmarks = "";
for($i=0;$i<count($related);$i++)
{
$questionmarks .= "?,";
}
$sql .= trim($questionmarks,",");
$sql .= ") LIMIT 3;";
// echo $sql; // outputs: SELECT id,title,pic1 FROM tbl_products WHERE id IN (?,?,?) LIMIT 3;
$q = $db->prepare($sql);
$q->execute($related); // edited this line no need to array($related), since $related is already an array
echo $q->rowCount();
https://3v4l.org/No4h1
(also if you want 4 records returned get rid of the LIMIT 3)
More elegantly you can use str_repeat to append your placeholders like this:
$related = array(1,2,3); // your "IN" values
$sql = "SELECT id,title,pic1 FROM tbl_products WHERE id IN (";
$sql .= trim(str_repeat("?,",count($related)),",");
$sql .= ") LIMIT 3;";
// echo $sql; // outputs: SELECT id,title,pic1 FROM tbl_products WHERE id IN (?,?,?) LIMIT 3;
$q = $db->prepare($sql);
$q->execute($related); // edited this line no need to array($related), since $related is already an array
echo $q->rowCount();
https://3v4l.org/qot2k
Also, by reading again your question i can guess that your $related variable is just a string with value comma-separated numbers like 1,40,6,99. If that's the case you need to make it an array. do: $related = explode($related,","); to make it an array of numbers. Then in your execute method pass $related as-is.

Search only reads one keyword: SQLSTATE[HY093]: Invalid parameter number

I'm having an issue with my search feature of my website. It's not handling search terms with more than one word. try a live example of the search bar problem over at http://mobile.mixtapemonkey.com/
search "It's Better This Way"
as soon as you type in B I get an error. other than that it works, what do you think the issue is?
if (empty($errors)) {
$name_explode = explode(' ', $name); // explode keywords to get each individual keyword, and put into array
$name_count = count($name_explode); // count keywords from array
foreach($name_explode as $name_single) {
$x++; // increment x each loop
$keyword = "%".$name_single."%";
$where .= '`keywords` LIKE :keyword'; // append to where clause
if ($name_count!=$x) {
$where .= ' AND '; // as long as keyword isn't the last, append AND.
}
}
$sql = "SELECT `name`, `thumb`, `id`, `title` FROM `mixtapes` WHERE ".$where." ORDER BY `id` DESC LIMIT 40";
$search = $db->prepare($sql); // perform query
$search->bindParam(':keyword', $keyword, PDO::PARAM_STR);
$search->execute();
$search_num_rows = $search->rowCount(); // get number of rows (results) returned
Please check the code I have added.the reason you got that error is due to your foreach loop. In that loop you add :keyword everytime. But that count dont match with the bindParam value count. So I changed you code and posting below
if (empty($errors)) {
$name_explode = explode(' ', $name); // explode keywords to get each individual keyword, and put into array
$name_count = count($name_explode); // count keywords from array
$x = 0;
foreach($name_explode as $name_single) {
$x++; // increment x each loop
$keyword[':keyword'.$x] = "%".$name_single."%";
$where .= '`keywords` LIKE :keyword'.$x; // append to where clause
if ($name_count!=$x) {
$where .= ' OR '; // as long as keyword isn't the last, append AND.
}
}
$sql = "SELECT `name`, `thumb`, `id`, `title` FROM `mixtapes` WHERE ".$where." ORDER BY `id` DESC LIMIT 40";
$search = $db->prepare($sql); // perform query
$search->execute($keyword);
$search_num_rows = $search->rowCount(); //

programming WHERE section in sql

Example i'm getting 2 variable from my db.($v1, $v2)
SELECT * FROM mytable WHERE id=$1 AND $v2
But in another example im getting 3 variable from db ($v1, $v2, $v3)at same query cause i proggramed it like it. In this condition how can i program WHERE statement of sql query
edit
WHERE id=$v1 AND $v2 AND $v3 // i want to add it And conditions automaticly
If you need to find any records which contain any of your variable you can use OR as follow
SELECT * FROM mytable WHERE id = $v1 OR id = $v2 OR id = $v3
If you are trying to select multiple ID values, use IN conditional:
SELECT * FROM mytable WHERE id IN ($v1, $v2, $v3)
EDIT: Asked for a flexible way to add values:
Assuming that this query is set by a function and this function will receive all ID values that should be in where clause. Something like:
<?php
$example_ids = array(1, 5, 7, 15, 22);
wherein($example_ids)
function wherein($ids){
// Select part
$query = "SELECT * FROM mytable ";
// Where statement
$where = " WHERE id IN (";
// For loop to use each value sent
// as a value in IN (...
for ($i=0; $i < count($ids); $i++) {
// Eg: $i = 0
// $ids[$i] = 1
//
// So:
// WHERE id IN (1
$where .= $ids[$i];
// If its not the last value then
// add a comma for SQL syntax
if ($i < count($ids) - 1)
/// Where id IN (1,
$where .= ",";
}
// At the end of the loop, $where must be
// something like:
// WHERE id IN (1,5,7,15,22
//
// Once again, for SQL syntax, the close )
$where .= ")";
// Finaly, the last step, putting all together:
// SELECT * FROM mytable WHERE id IN (1,5,7,15,22)
$query .= $where;
}

Categories