MySQL SELECT with multiple conditions - php

When searching for available cars, I'm trying to SELECT from cars table WHERE the IDs do not include the already booked cars.
For that, I used :
$result = $pdo->prepare("SELECT * FROM cars
WHERE id NOT IN (" . implode(',', $bookedCarIds) . ")
AND (location = '$fromlocation' AND fromdate BETWEEN '$fromdate' AND '$todate')
");
$result->execute();
$availableCars = [];
$availableCars = $result->fetchAll();
Which works perfectly fine.
However, the issue arises when there are no already booked cars in the selected location and dates (where $bookedCarsIds is NULL). To deal with this issue, I used the following code, which throws a syntax error.
$result = $pdo->prepare("SELECT * FROM cars
WHERE id NOT IN (" . implode(',', $bookedCarIds) . ") AND
(location = '$fromlocation' AND fromdate BETWEEN '$fromdate' AND '$todate')
OR WHERE location = '$fromlocation' AND fromdate BETWEEN '$fromdate' AND '$todate'
");
$result->execute();
$availableCars = [];
$availableCars = $result->fetchAll();
How can I solve this issue by making changes in this MySQL query alone, without altering the rest of my code?
Thank you.

If you are using PDO, you need to create a dynamic WHERE clause with parameters like in this example:
$whereClause = [];
$params = [];
if (!empty($bookedCarIds)) {
$whereClause[] = 'id NOT IN (?' . str_repeat(', ?', count($bookedCarIds) - 1) . ')';
$params = $bookedCarIds;
}
if (!empty($fromlocation)) {
$whereClause[] = 'location = ?';
$params[] = $fromlocation;
}
if (!empty($fromdate) && !empty($todate)) {
$whereClause[] = 'fromdate BETWEEN ? AND ?';
$params[] = $fromdate;
$params[] = $todate;
}
$whereClause = !empty($whereClause) ? 'WHERE ' . implode(' AND ', $whereClause) : '';
$result = $pdo->prepare("SELECT * FROM cars $whereClause");
$result->execute($params);
$availableCars = $result->fetchAll();

Related

How to create an SQL query using an array?

I have an Array with data and I want to create an SQL statement (In which I am going to use where to where in).
I have tried to make query but no success.
SQL will be like :
SELECT *
FROM documents
WHERE category = "recruitment"
AND sub_category in("forms")
OR category = "onboarding"
AND sub_category IN ("sop")
OR category = "policies"
AND sub_category IN("forms");
and Array is this :
{"Recruitment":["Forms"],"Onboarding":["Sop"],"Policies":["Forms"]}
I have tried this code :
foreach ($db_sub as $data=>$key){
$query = "where document.category = ".$data." or where document.sub_category in ('".$key."')";
}
But getting error array to string conversion. Plz help me.
Thanks in advance.
Error :
You are trying to concatenate a string with an array. Since your array's values are also arrays, you will have to convert them to string first. It could be done with the join function.
foreach ($db_sub as $data=>$key){
$query = "where document.category = ".$data." or where document.sub_category in ('".join(", ", $key)."')";
}
Now you shouldn't get array to string conversion error.
Use this example to create the required SQL query:
$whereClause = [];
$params = [];
foreach ($db_sub as $data => $key){
if (!is_array($key)) $key = [$key];
$whereClause[] = '(documents.category = ? AND documents.sub_category IN (?' . str_repeat(', ?', count($key) - 1) . '))';
$params[] = $data;
$params = array_merge($params, $key);
}
$whereClause = !empty($whereClause) ? 'WHERE ' . implode(' OR ', $whereClause) : '';
$query = "SELECT * FROM documents $whereClause";
$sth = $pdo->prepare($query);
$sth->execute($params);
$result = $sth->fetchAll();
Here variable $pdo is a PDO object

Selecting rows where out of two columns one is null

Out of two search selections if a visitor select one only there is no search result. Following is my sql query:
$sql = "SELECT * FROM table WHERE column1='$column1' AND column2 ='$column2' ORDER BY id DESC
If I use 'OR' or otherwise I got wrong result in pagination. What should be right coding if a visitor opted only one criteria to search he will get result in first and subsequent pages?
In PHP construct your query:
$where = [];
$params = [];
if (!empty($column1)) {
$where[] = 'column1 = :column1';
$params[':column1'] = $column1;
} else {
$where[] = 'column1 IS NULL';
}
if (!empty($column2)) {
$where[] = 'column2 = :column2';
$params[':column2'] = $column2;
} else {
$where[] = 'column2 IS NULL';
}
if (!empty($where)) {
$pdo
->prepare("SELECT * FROM table WHERE ".implode(' AND ', $where))
->execute($params);
}
If you allow selection only by one column, remove else parts
A fast solution is that you can put the filters into a variable checking if the values of $column1 or $column2 it's filled and add after that in the SELECT clause:
$where_column = 'WHERE ';
if ($column1 != false)
$where_column .= "column1='$column1'";
if ($column2 != false) {
if ($where_column != 'WHERE') {
$where_column .= "AND column2='$column2'";
else
$where_column = "column2='$column2'";
}
}
$sql = "SELECT * FROM table $where_column ORDER BY id DESC

[PHP]How to use foreach to add AND or OR statement into a sql query?

Hi guys I am trying to perform multiple criteria search in my PHP, so I wanted to use foreach to add AND statement automatically based on the number of user selection. Below is my code:
$sub = trim(ucfirst($_POST['sub']));
$any = trim(ucfirst($_POST['key']));
$count = 0;
$query = "SELECT P.PTY_ID, P.PTY_UNITNUM, P.PTY_STREET, P.PTY_POSTCODE, P.PTY_SUBURB,P.PTY_CITY, T.P_TYPE_NAME,
L.SALE_PRICE, L.AVAILABILITY, R.PTY_ID,F.FEAT_ID, R.FEAT_ID,F.FEAT_NAME
FROM PROPERTY_TYPE T, PROPERTY P, LISTINGS L, PROPERTY_FEATURE R, FEATURE F
WHERE P.P_TYPE_ID = T.P_TYPE_ID(+)
AND P.PTY_ID = L.PTY_ID(+)
AND R.FEAT_ID= F.FEAT_ID
AND R.PTY_ID= P.PTY_ID
AND REGEXP_LIKE (P.PTY_SUBURB,'$sub')
AND REGEXP_LIKE (P.PTY_STREET||P.PTY_POSTCODE,'$any')
OR(
".foreach ($_POST["check"] as $feat_id) {
if ($count != 0)
echo "AND";
echo "FEAT_ID = ' . $feat_id . '";
$count++;
}.")";
$stmt = oci_parse($conn, $query);
oci_execute($stmt);
So in my case, I want to search properties by suburb, some keywords like postcode and street name, as well as features. In order to respond to the users' selection on multiple features, I tried to use the foreach loop to print "AND FEAT_ID = (posted id)", but I got a syntax error after the "OR" part till the end of the query, saying that some expression is expected.
I am quite new in PHP study at the moment. Can someone point out the correct format for me? Cheers(:
I'm not sure 100% what your issue is, but i would clean it up a little. Again this is my coding style so others may still think this can be improved.
$sub = trim(ucfirst($_POST['sub']));
$any = trim(ucfirst($_POST['key']));
$count = 0;
$or = "";
if (isset($_POST["check"]))
{
$featArray = [];
foreach ($_POST["check"] as $feat_id)
{
array_push($featArray, "FEAT_ID = " .$feat_id);
}
$or = implode(' AND ', $featArray);
}
$query = "SELECT P.PTY_ID, P.PTY_UNITNUM, P.PTY_STREET, P.PTY_POSTCODE, P.PTY_SUBURB,P.PTY_CITY, T.P_TYPE_NAME,
L.SALE_PRICE, L.AVAILABILITY, R.PTY_ID,F.FEAT_ID, R.FEAT_ID,F.FEAT_NAME
FROM PROPERTY_TYPE T, PROPERTY P, LISTINGS L, PROPERTY_FEATURE R, FEATURE F
WHERE P.P_TYPE_ID = T.P_TYPE_ID(+)
AND P.PTY_ID = L.PTY_ID(+)
AND R.FEAT_ID= F.FEAT_ID
AND R.PTY_ID= P.PTY_ID
AND REGEXP_LIKE (P.PTY_SUBURB, '$sub')
AND REGEXP_LIKE (P.PTY_STREET||P.PTY_POSTCODE, '$any')";
if ($or != null)
$query .= " OR (" . $or . ")";
$stmt = oci_parse($conn, $query);
oci_execute($stmt);
I'd also check that your $_POST['sub'] and $_POST['key'] or set.
#Lee Stevens
Thanks for the idea! The use of .= you mentioned solve my problem perfectly.
To fit my use i have modified the codes into this:
<?php
$sub = trim(ucfirst($_POST['sub']));
$any = trim(ucfirst($_POST['key']));
$count = 0;
$and = "";
/* All the same above */
if (isset($_POST["check"]))
{
$featArray = [];
foreach ($_POST["check"] as $feat_id)
{
array_push($featArray, "AND F.FEAT_ID = " .$feat_id);
/* Add an "AND" here coz the original codes can't print */
/* the "AND" for some reasons */
}
$and = implode(" ", $featArray);
/* Remove "AND" here */
}
$query = "SELECT P.PTY_ID, P.PTY_UNITNUM, P.PTY_STREET, P.PTY_POSTCODE, P.PTY_SUBURB,P.PTY_CITY, T.P_TYPE_NAME,
L.SALE_PRICE, L.AVAILABILITY, R.PTY_ID,F.FEAT_ID, R.FEAT_ID,F.FEAT_NAME
FROM PROPERTY_TYPE T, PROPERTY P, LISTINGS L, PROPERTY_FEATURE R, FEATURE F
WHERE P.P_TYPE_ID = T.P_TYPE_ID(+)
AND P.PTY_ID = L.PTY_ID(+)
AND R.FEAT_ID= F.FEAT_ID
AND R.PTY_ID= P.PTY_ID
AND REGEXP_LIKE (P.PTY_SUBURB,'$sub')
AND REGEXP_LIKE (P.PTY_STREET||P.PTY_POSTCODE,'$any')";
if (!empty($and))
$query.="$and";
$stmt = oci_parse($conn, $query);
oci_execute($stmt);
?>

SQL Injection - is this query secure?

I have a a page that appends different parameters to the URL that are used for the query.
For example
http://www.example.com/search.php?category=Schools&country[]=Belgium&country[]=Czech+Republic
My code is like this
if(isset($_GET['country'])){
$cties = "'" . implode("','", $_GET['country']) . "'";
}
else {
$cties = "'Albania','Andorra','Austria','Belarus','Belgium','Bosnia & Herzegovina','Bulgaria','Croatia','Czech Republic','Denmark','Estonia','Faroe Islands','Finland','France','Germany','Gibraltar','Great Britain','Greece','Hungary','Iceland','Ireland','Isle of Man','Italy','Latvia','Liechtenstein','Lithuania','Luxembourg','Macedonia','Malta','Moldova','Monaco','Montenegro','Netherlands','Norway','Poland','Portugal','Serbia','Romania','San Marino','Slovakia','Slovenia','Spain','Sweden','Switzerland','Ukraine','United Kingdom'";
}
if(isset($_GET['category'])){
$cat = $_GET['category'];
}
else{
$cat = " ";
}
try{
// create the Prepared Statement
$stmt = $con->prepare("SELECT * FROM MyTable
WHERE MyDate >= DATE(NOW())
AND (Category=:cat or '' = :cat)
AND Country IN ($cties)
ORDER BY MyDate ASC");
$stmt->bindValue(':cat', $cat, PDO::PARAM_STR);
$stmt->execute();
I was wondering if this query is secure and if not, what I am doing wrong.
Thanks in advance!
I finally got it (thanks to Your Common Sense):
if(isset($_GET['country'])){
$arr = $_GET['country'];
}
else {
$arr = array('Albania','Andorra','Austria','Belarus','Belgium','Bosnia & Herzegovina','Bulgaria','Croatia','Czech Republic','Denmark','Estonia','Faroe Islands','Finland','France','Germany','Gibraltar','Great Britain','Greece','Hungary','Iceland','Ireland','Isle of Man','Italy','Latvia','Liechtenstein','Lithuania','Luxembourg','Macedonia','Malta','Moldova','Monaco','Montenegro','Netherlands','Norway','Poland','Portugal','Serbia','Romania','San Marino','Slovakia','Slovenia','Spain','Sweden','Switzerland','Ukraine','United Kingdom');
}
if(isset($_GET['category'])){
$cat = $_GET['category'];
}
else{
$cat = " ";
}
// create the Prepared Statement
$in = str_repeat('?,', count($arr) - 1) . '?';
$sql = "SELECT * FROM MyTable WHERE MyDate >= DATE(NOW())
AND Country IN ($in)
AND (Category=? or '' = ?)
ORDER BY MyDate ASC";
$stmt = $con->prepare($sql);
$arr[] = $cat; // adding category to array
$arr[] = $cat; // we need it twice here
// finally - execute
$stmt->execute($arr);
Yeah, Now I see your problem. Well, PDO is not too convenient a library for such a task. So, first of all I'll show you how it can be done with my own library:
$sql = "SELECT * FROM MyTable WHERE MyDate >= CURDATE()
AND (Category=?s or '' = ?s)
AND Country IN (?a)
ORDER BY MyDate ASC"
$data = $db->getAll($sql, $cat, $cat, $_GET['country']);
But I quite realize that you all so inclined to familiar methods. Well, let's elaborate with ugly PDO
First of all, what is the goal? The goal is
to create the query that contains placeholders for all the data. I'll stick to positional placeholders as they are easier to implement.
To create an array with all the variables that have to be bound to placeholders
It seems we need two placeholders for category and some unknown number fro cities. All right, this line will create a string of placeholders:
$in = str_repeat('?,', count($arr) - 1) . '?';
which we are going to insert into query.
// $arr is array with all the vars to bind. at the moment it contains cities only
$arr = $_GET['country'];
// creating string of ?s
$in = str_repeat('?,', count($arr) - 1) . '?';
// building query
$sql = "SELECT * FROM MyTable WHERE MyDate >= DATE(NOW())
AND Country IN ($in)
AND (Category=? or '' = ?)
ORDER BY MyDate ASC";
$stm = $db->prepare($sql);
$arr[] = $_GET['category']; // adding category to array
$arr[] = $_GET['category']; // we need it twice here
// finally - execute
$stm->execute($arr);
$data = $stm->fetchAll();
No, the SQL code could be injected in the $_GET['country'] parameter. You don't escape it anywhere.
See PHP PDO: Can I bind an array to an IN() condition?

Problems with using PDO to perform an advanced search query

I have the following code and all of the search functions work except for the title field. So I can search by genre, date, location etc... but not by title. When attempting to search by title nothing is returned at all. Can anyone help me with this?
Also, is there a more efficient way to count all the fields before limiting it for use in pagination later on?
$today = date("Y-m-d");
$query = "SELECT * FROM TABLE_NAME WHERE Date >= '$today'";
$bind = Array();
if ($_GET["Title"] && $_GET["Title"] != "") {
$query .= " and Title like %?%";
$bind['Title'] = $_GET['Title'];
}
if ($_GET["Genre"] && $_GET["Genre"] != "") {
$query .= " and Genre like %?%";
$bind['Genre'] = $_GET['Genre'];
}
if ($_GET["Location"] && $_GET["Location"] != "") {
$query .= " and Location like %?%";
$bind['Location'] = $_GET['Location'];
}
if ($_GET["Date"] && $_GET["Date"] != "") {
$query .= " and Date = %?%";
$bind['Date'] = $_GET['Date'];
}
$stmt = $db->prepare($query);
$stmt->execute($bind);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
$num = count($rows);
$query .= " ORDER BY Date LIMIT $limit, 9";
$stmt = $db->prepare($query);
$stmt->execute($bind);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
Edit: After everyone's help I thought I would post my now revised code for future reference. It turns out the other fields were not working, but instead due to the if statement all this was nested in the code simply wasn't being executed.
$today = date("Y-m-d");
$query = "SELECT * FROM TABLE_NAME WHERE Date >= '$today'";
$countq = "SELECT count(*) FROM TABLE_NAME WHERE Date >= '$today'";
$bind = Array();
if ($_GET["Title"] && $_GET["Title"] != "") {
$query .= " and Title like :title";
$countq .= " and Title like :title";
$bind[':title'] = "%{$_GET['Title']}%";
}
if ($_GET["Genre"] && $_GET["Genre"] != "") {
$query .= " and Genre like :genre";
$countq .= " and Genre like :genre";
$bind[':genre'] = "%{$_GET['Genre']}%";
}
if ($_GET["Location"] && $_GET["Location"] != "") {
$query .= " and Location like :loc";
$countq .= " and Location like :loc";
$bind[':loc'] = "%{$_GET['Location']}%";
}
if ($_GET["Date"] && $_GET["Date"] != "") {
$query .= " and Date = :date";
$countq .= " and Date = :date";
$bind[':date'] = "{$_GET['Date']}";
}
$stmt = $db->prepare($countq);
$stmt->execute($bind);
$rows = $stmt->fetchAll();
$num = count($rows);
$query .= " ORDER BY Date LIMIT $limit, 9";
$stmt = $db->prepare($query);
$stmt->execute($bind);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
all of the search functions work
With the given query it is not true
From PDO tag wiki:
placeholders cannot represent an arbitrary part of the query, but a complete data literal only. Neither part of literal, nor whatever complex expression or a syntax keyword can be substituted with prepared statement.
Prepare FULL literal first: $name = "%$name%"; and then bind it.
As for the "more" efficient method for pagination - yes, oh yes.
With your current way of counting data you don't actually need other queries. as you have ALL the data already and can paginate it as well.
But of course it will pollute all the memory soon. So, if you want to get a count of rows from database, get the very count: run the same query but instead of SELECT * make it "SELECT count(*)
There are not any errors returned, that's why I am so confused
From PDO tag wiki again:
It is essential to set ERRMODE_EXCEPTION as a connection option as it will let PDO throw exceptions on connection errors. And this mode is the only reliable way to handle PDO errors.

Categories