Advice on Prepared-Statements and Joins - php

I'm trying to build a back end solution to a web page project for a class i'm taking. The class doesn't cover anything about back end coding so I've had to learn everything myself. I want to understand how to utilize prepared statements and joins in the PHP code below. There are three tables in my database, but i only want back the data from one of them and only sometimes need to reference the other two to hone in my search.
The code currently works as expected, but is vulnerable to sql injection. I'm getting three form inputs from the user: a text input ($_POST["spell_name"]), a select element ($_POST["classList"]), and some checkboxes ($_POST["school"]). It still needs to work when the checkbox sends an empty array (none of the checkboxes are checked) and the select element sends the default value "all".
$sql = "SELECT * FROM dnd5_spells WHERE ";
if($_POST["classList"] != "all"){
$sql .= "spell_id= ANY(SELECT spell_id FROM dnd5_class_spells WHERE class_id = ANY(SELECT class_id FROM dnd5_classes WHERE class_name='{$_POST["classList"]}')) AND";
};
$sql .= " spell_name LIKE '%{$_POST["spell_name"]}%'";
if(!empty($_POST["school"])){
$sql .= " AND (";
$spellschools = $_POST["school"];
$valueLength = count($spellschools);
for ($x = 0; $x < $valueLength; $x++) {
if ($x>0) {
$sql.= " OR";
};
$sql .= " spell_type" . " LIKE" . " '%" . $spellschools[$x] . "%'";
};
$sql .= ")";
};
$result = mysqli_query($conn, $sql);
How do i make a prepared statement when i can receive all or none of the possible input values and i don't know what i'm getting ahead of time (in my case sending the form data blank returns the entire table and this is good)? Can i iterate over an array into a prepared statement when the array can either be empty or have multiple values? Should i be concerned about including sections of the sql query when that part of the query isn't needed?
Also, i know my use of ANY could possibly be replaced with a JOIN, but i couldn't wrap my head around it and it's less important to me than figuring out the prepared statement issue.

Here is the solution I found. I needed to use PDO then build my statement as I went with prepared statements in mind, placing ? where I wanted my input data, and at the same time build an array with the corresponding data values. I doubt it's perfect but it works just as well as my initial code and is less susceptible to SQL injection.
$sql = "SELECT * FROM dnd5_spells WHERE ";
$parameters = [];
if($_POST["classList"] != "all"){
$sql .= "spell_id= ANY(SELECT spell_id FROM dnd5_class_spells WHERE class_id = ANY(SELECT class_id FROM dnd5_classes WHERE class_name= ?)) AND";
$parameters[] = $_POST["classList"];
};
$sql .= " spell_name LIKE ?";
$parameters[]= "%" . $_POST["spell_name"] . "%";
if(!empty($_POST["school"])){
$sql .= " AND (";
$spellschools = $_POST["school"];
$valueLength = count($spellschools);
for ($x =0; $x < $valueLength; $x++){
if ($x>0){
$sql.= " OR";
};
$sql.= " spell_type LIKE ?";
$parameters[] ="%" . $spellschools[$x] . "%";
};
$sql .= ")";
};
//bind the peramaters into the prepared statment
$stmt = $conn->prepare($sql);
$stmt->execute($parameters);
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);

Related

mysql dynamic queries without clause where

In the following example there is a base query. Other parameters can be dynamically added to complete the query.
However, my base query has no clause WHERE.
What is the best way to deal with it.
If I use in the base query, for example, WHERE 1 = 1, it seems to solve, but I have some doubts that is a correct solution.
$myQuery = "SELECT fr.oranges, fr.aplles, fr.bananas,
FROM fruits fr
LEFT JOIN countrys ct ON fr.id_fruit = ct.id_fruit";
if(!empty($countrys){
$myQuery .= " AND countrys = ? ";
}
if(!empty($sellers){
$myQuery .= " AND seller = ? ";
}
$myQuery .=" GROUP BY fr.id_fruit ORDER BY fr.fruit ASC";
Edited: I fixed a writing gap from $empty to empty.
The WHERE 1=1 is a simplistic hack that works well because it simplifies your code. There is a great post here which explains the performance implications of WHERE 1=1. The general consensus is it will have no effect on performance.
Also, slight note ($empty) is probably not a function you've defined. I think you want empty(). You could write it like this:
$myQuery = "SELECT fr.oranges, fr.aplles, fr.bananas,
FROM fruits fr
LEFT JOIN countrys ct ON fr.id_fruit = ct.id_fruit";
$where = [];
if(!empty($countrys){
$where[] = "countrys = ?";
}
if(!empty($sellers){
$where[] = "seller = ?";
}
if (!empty($where)) {
$myQuery .= " WHERE " . implode(" AND ", $where);
}
$myQuery .= " GROUP BY fr.id_fruit ORDER BY fr.fruit ASC";
You can use an array to control your SQL like this:
$where = [];
if(!$empty($countrys){
$where[] = " countrys = ? ";
}
if(!$empty($sellers){
$where[] = " seller = ? ";
}
if(count($where) > 0) {
$myQuery .= " WHERE ".implode('AND', $where);
}

How to debug the concatenation of a SQL query in PHP?

There's something with my query, but I cannot manage to find what.
$keys = array_keys($fields);
$values = array_values($fields);
$sql = "UPDATE " .$table. " SET " .implode("`, `", $keys) ."='".implode("', '", $values) . "' WHERE id={$id}";
And it shows as : UPDATE users SET name, password'Rick is vets', 'sdfg' WHERE id=5
But it has to show as : UPDATE users SET name = 'Rick is vets', password='sdfg' WHERE id=5
Try looping through the $fields array to create an update string like this:
$update_string='';
foreach ($fields as $key=>$value)
{
$update_string .= $key."='$value', ";
}
Then remove the last comma character from the string using rtrim() function:
$update_string = rtrim($update_string, ", ");
Then your query becomes:
$sql = "UPDATE " .$table. " SET " .$update_string. " WHERE id={$id}";
This is just to illustrate the concept since your code might still be
open to SQL injection attacks, in which case you should use prepared
statement.
$setString='';
foreach($fields as $k=>$v){
$setString .=$k." = '".$v."', ";
}
$setString=rtrim($setString,', ');
include $setString in query

PHP: How to pass multiple values to SELECT query

I am new to PHP and hope someone can help me with this.
I currently use the below lines to retrieve a value from a db and to output it as an array with the item's ID and value which works as intended.
Now I would need to do the same for multiple items so my input ($tID) would be an array containing several IDs instead of just a single ID and I would need the query to do an OR search for each of these IDs.
I was thinking of using a foreach loop for this to append " OR " to each of the IDs but am not sure if this is the right way to go - I know the below is not working, just wanted to show my thoughts here.
Can someone help me with this and tell me how to best approach this ?
My current PHP:
$content = "";
$languageFrm = $_POST["languageFrm"];
$tID = $_POST["tID"];
$stmt = $conn->prepare("SELECT tID, " . $languageFrm . " FROM TranslationsMain WHERE tID = ? ORDER BY sortOrder, " . $languageFrm);
$stmt->bind_param("s", $tID);
$stmt->execute();
$result = $stmt->get_result();
while($arr = $result->fetch_assoc()){
$content[] = array("ID" => $arr["tID"], "translation" => $arr[$languageFrm]);
}
My thought:
foreach($tID as $ID){
$ID . " OR ";
}
Many thanks for any help,
Mike
There are two approaches, assuming $tID is an array of IDs
Using MySQL IN() clause
This will work also when $tID is not an array, but a single scalar value.
$tID = array_map('intval', (array)$tID); // prevent SQLInjection
if(!empty($tID)) {
$query .= ' WHERE tID IN(' . implode(',', $tId) . ')';
} else {
$query .= ' WHERE 0 = 1';
}
Using OR clause, as you suggested
A bit more complicated scenario.
$conds = array();
foreach($tID as $ID) {
$conds[] = 'tID = ' . intval($ID);
}
if(!empty($conds)) {
$query .= ' WHERE (' . implode(' OR ', $conds) . ')';
} else {
$query .= ' WHERE 0 = 1';
}
As per above conditions you can try with implode();
implode($tID,' OR ');
You can also use IN condition instead of OR something like this.
implode($tID,' , ');

Setting up SQL queries with multiple parameters

I need to set up a SQL query with multiple parameters that are being pulled from the URL. So far I can only get it to work with the there is only one item in the URL.
My default query to pull in all the content
$sql = "SELECT ";
$sql .= "* ";
$sql .= "FROM ";
$sql .= "cms_site_content ";
$sql .= "WHERE ";
$sql .= "1";
I then check if anything was passed through the URL and retrieve it.
if (isset($_GET["d"])) {
$d=$_GET["d"];
Inside the if statement, I break the values passed as "d" into separate items
$newD = explode(',',$d);
$countD = count($newD);
foreach($newD as $discipline) {
if ($countD == 1) {
$sql .= " AND";
$sql .= " discipline='".$discipline."'";
}
My problem is getting the SQL to work if there is more than one discipline value. It should read something like this:
SELECT * FROM cms_site_content WHERE 1 AND discipline="value"
however if there's more than one discipline value, it should read:
SELECT * FROM cms_site_content WHERE 1 AND discipline="value OR discipline="value2" OR discipline="value3"
Is there a more efficient way to write this? I can't figure out how to insert the OR into the foreach statement.
Save all discipline values in an array;
$discipline_arr = array();
foreach($newD as $discipline) {
$discipline_arr[] = $discipline;
// by the way, don't forget to escape for sql injection
// mysql_escape_string is the depracated one, u can use that if u have no
// other choice
}
Then in your sql, add them as discipline in ('value1','value2', 'etc ...') condition (that is for strings, for numeric types it would be like discipline in (1,2,3,4, etc)
$sql = " SELECT * FROM cms_site_content WHERE 1 " .
(empty($discipline_arr) ? "" : "and
discipline in ('". implode("','" , $discipline_arr). "') ") ;
Link to escaping
http://tr1.php.net/manual/en/function.mysql-escape-string.php
Assuming the rest of your query is in tact. Simply store all of your discipline values in an array as follows, then feed the $discipline_string to your $sql query:
$discipline_ary = array('option1', 'option2', 'option3');
$discipline_string = "";
for($i=0; $i < count($discipline_ary); $i++){
$discipline_string .= " discipline = '" . $discipline[$i] . "' ";
if($i+1 == count($discipline_ary)){
break;
}else{
$discipline_string .= " OR "
}
}

how to make sql filters efficient php

I have 4 filters for a catalog (name , id, date, price). Those are inputs from the user to see specific data from the database. Those 4 filters are going to produce 4^2 (16) sql_queries on php , because some of the filters may be null. Is there a better way to make queries less ?
example:
if(isset($_POST['filters']))
{
$date = $_POST['date'];
$timi = $_POST['timi'];
$employee = $_POST['dropdown_users'];
$proion =$_POST['dropdown_proionta'];
$query = ("SELECT * FROM id_of_orders WHERE username='$employee' AND price = '$timi' AND time = '$date' AND proion='$proion'");
$result=mysql_query($query);
while($row= mysql_fetch_array($result))
{
echo $row['id_order'] . " " . $row['time'] . '<br>';
}
}
I think you are looking for something like this:
$query = "SELECT * FROM id_of_orders WHERE 1=1";
if(!empty($employee))
$query .= " AND username='$employee'";
if(!empty($timi))
$query .= " AND price='$timi'";
if(!empty($date))
$query .= " AND time='$date'";
if(!empty($proion))
$query .= " AND proion='$proion'";
This way no query conditions are added for empty filters, while entered filters will be used as conditions for the results.

Categories