PHP 'Where' query conditional on 'If' 'isset' - php

I am new to PHP so any help on the following would be really appreciated:
I am trying to run a query with a WHERE condition, but I only want to apply the where condition if the variable has been set (via a radio button).
I basically want to be able to say - if a variable is not set, do not include it in the where condition. The complication is that I will be doing this with 3+ variables...
At its most basic - here is the code I have so far:
if(isset($_POST['category'])){
$category = $_POST['category'];}
if(isset($_POST['brand'])) {
$brand = $_POST['brand'];
}
{
$q = 'SELECT name, category, brand, price, imageurl, purchaselink FROM clothes WHERE category = "'.$_POST['category'].'" AND brand = "'.$_POST['brand'].'"';
[truncated]
Thanks very much in advance!

You can do this with an array, but first of all DO NOT BUILD QUERIES THIS WAY since you'll be vulnerable to SQL injection. Use PDO instead.
The idea is to have a list of suitable conditions:
$conds = [ 'brand', 'category', 'name' ];
$where = [ ]; // Empty array
foreach ($conds as $cond) {
if (!empty($_POST[$cond])) {
$sql = "({$cond} = ?)"; // We use PDO and bound values.
$where[$sql] = $_POST[$cond];
// In *deprecated* MySQL we would use at least
// $sql = "({$cond} = '" . mysql_real_escape_string($_POST[$cond]) . "')";
// $where[$sql] = true;
}
}
// Now we have a list of pairs:
// brand = ? => 'MyBrand',
// name = ? => 'MyName',
if (!empty($where)) {
$sql_string .= ' WHERE (';
$sql_string .= implode( ' AND ', array_keys($where) );
$sql_string .= ')';
}
// $sql_string is now SELECT ... WHERE ( (brand=?) AND (name=?) ... )
// Using the MySQL version, we would have ... WHERE ( (brand='MyBrand') AND ... ) )
// With PDO we PREPARE the query using sql_string
// http://dev.mysql.com/doc/apis-php/en/apis-php-pdo-mysql.html
// http://www.php.net/manual/en/intro.pdo.php
// We need an open PDO connection saved into $pdo
$stmt = $pdo->prepare ($sql_string);
// Then we execute the query.
// Bind the values to array_values($where).
$stmt->execute( array_values($where) );
while ($tuple = $stmt->fetch(PDO::FETCH_ASSOC)) {
...
}
A shorter way, MySQL only (since it does not distinguish between keys and values) would be
$where = [ ]; // empty array()
foreach ($conds as $cond) {
if (empty($_POST[$cond])) {
continue;
}
// THIS IS NOT SECURE. See e.g. http://johnroach.info/2011/02/17/why-mysql_real_escape_string-isnt-enough-to-stop-sql-injection-attacks/
$escaped = mysql_real_escape_string($_POST[$cond]);
$where[] = "({$cond} = '{$escaped}')";
}
$query = "SELECT ...";
if (!empty($where)) {
$query .= " WHERE (" . implode(' AND ', $where) . ")";
}
This approach has the additional advantage that you can have the 'AND' parameterized - the user can choose whether have the conditions ANDed or ORed via a radiobutton:
$and_or = ('OR' == $_POST['andor']) ? ' OR ' : ' AND ';
$query .= " WHERE (" . implode($and_or, $where) . ")";
Note that the actual value of 'andor' is NOT used -- if it is an OR, all well and good, ' OR ' is used. Anything else that might be accidentally sent in a POST by a customer, such as "--; DROP TABLE Students;" , is considered to mean ' AND '.

you have to incluide all inside if(){}
if(isset($_POST['category']) && isset($_POST['brand'])){
$q = 'SELECT name, category, brand, price, imageurl, purchaselink FROM clothes WHERE category = "'.$_POST['category'].'" AND brand = "'.$_POST['brand'].'"';
}

I use this technique to make my filters :
$q = 'SELECT name, category, brand, price, imageurl, purchaselink FROM clothes WHERE 1=1 ';
if(isset($_POST['category'])) $q .= ' AND category ="'.$_POST['category'].'"';
if(isset($_POST['brand'])) $q .= ' AND brand = "'.$_POST['brand'].'"';
// here you can add other filters. :) be happy

Related

PHP filters combining into one SQL query

I'm trying to filter through my database according to filters done by visitors.
$query = "select * from Sheet1 where";
//filter query start
if (!empty($brand)) {
$branddata = implode("','", $brand);
//testing removing query
$query .= " Brand in('$branddata') and";
}
if (!empty($model)) {
$modeldata = implode("','", $model);
//testing removing query
$query .= " Model in('$modeldata') and";
}
/* if(!empty($model) && empty($brand)){
} */
if (!empty($spower) && !empty($epower)) {
$query .= " Power>='$spower' and Power<='$epower' and";
}
if (!empty($sprice) && !empty($eprice)) {
$query .= " Doors>='$sprice' and Doors<='$eprice'";
}
$rs = mysqli_query($conn, $query) or die("Error : " . mysqli_error($conn));
The result I wish to get is a sql query that works and has correct syntax. Such as select * from Sheet1 where Doors>='$sprice' and Doors<='$eprice', if the visitor is filtering by price.
Currently, my code is made so that it simply adds a certain string to the variable. This means that if you don't filter by model, it skips model, because the model variable is empty. The problem comes to if you filter by power, the SQL will become select * from Sheet1 where Power>='$spower' and Power<='$epower' and. Obviously this doesn't work, so I need help in making the code make sure it works for every combination of filters.
Append $query .= " 1 = 1"; at the end. I did some modification in your given code. Have a look.
<?php
$query = "SELECT * FROM `Sheet1` WHERE";
//filter query start
if(!empty($brand)){
$branddata = implode("','",$brand);
$query .= " (Brand in('$branddata')) AND";
}
if(!empty($model)){
$modeldata = implode("','",$model);
$query .= " (Model in('$modeldata')) AND";
}
if(!empty($spower) && !empty($epower)){
$query .= " (Power>='$spower' AND Power<='$epower') AND";
}
if(!empty($sprice) && !empty($eprice)){
$query .= " (Doors>='$sprice' AND Doors<='$eprice') AND"; //Added 'AND'
}
$query .= " 1 = 1"; //Added new line
$rs = mysqli_query($conn,$query) or die("Error : ".mysqli_error($conn));
?>
Add AND on each query appended in if conditions. Then, at last add $query .= " 1 = 1";. Which will save you from extra AND coming at the end. If none of the conditions satisfy, then your query will be SELECT * FROM Sheet1 WHERE 1 = 1. Simple. And, don't forget to differentiate between conditions in query. Differentiate your conditions like how I did by opening and closing brackets.
I would do it this way
$filters=array();
if(!empty($brand)){
$branddata =implode("','",$brand);
//testing removing query
$filters[]= " Brand in('$branddata')";
}
if(!empty($model)){
$modeldata =implode("','",$model);
//testing removing query
$filters[]= " Model in('$modeldata') and";
}
if(!empty($spower) && !empty($epower)){
$filters[]= " Power>='$spower' and Power<='$epower' and";
}
if(!empty($sprice) && !empty($eprice)){
$filters[]= " Doors>='$sprice' and Doors<='$eprice'";
}
$query = "select * from Sheet1 where";
foreach ($filters as $filter) {
$query.=' AND '.$filter;
}
$rs = mysqli_query($conn,$query) or die("Error : ".mysqli_error($conn));

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 "
}
}

MySQL batch insert fails, no error

I'm trying to create a php function to split up the data into batches as it fails when I try to insert them fairly quickly.
I'm trying to insert thousands of records of user-data into a different format in the same database, later to be exported to a seperate database. However the query fails.
Based on comments and answers below I've updated the code to the following. Still fails, though.
The code inserting values:
function insertUsers( $users ){
error_reporting(E_ALL);
ini_set('display_errors',1);
global $pdo;
//insert into database
$i = 0;
$base = 'INSERT INTO tth_user_accounts (user_login, user_pass, user_email, user_registered,
user_firstname, user_lastname ) VALUES ';
$sql = '';
var_dump($users);
while( $i < count( $users ) ){
$sql = $sql . ' ("' .
$users[$i]['user_login'] . '", "' .
$users[$i]['user_pass'] . '", "' .
$users[$i]['user_email'] . '", "' .
$users[$i]['user_registered'] . '", "' .
$users[$i]['meta_value']['first_name'] . '", "' .
$users[$i]['meta_value']['last_name'] . '")';
if (!( $i % 25 === 0 )){
$sql = $sql . ', ';
}
if ($i % 25 === 0) {
//execute $base + $query here
$sql = $base . $sql;
$query = $pdo->prepare( $sql );
echo 'check query: <br />';
print_r( $query );
if( $query->execute() ){
echo '50 users succesfully added to the database';
} else {
echo 'Query failed: ';
print_r( $pdo->errorInfo() );
echo '<br />';
}
$sql = ''; //Re-init query string
}
$i++;
}
if ( strlen( $sql ) > 0 ) { // Execute remainder, if any
//execute $base + $query here
$sql = $base . $sql;
$query = $pdo->prepare( $sql );
echo 'check query: <br />';
print_r($query);
if( $query->execute() ){
echo 'User succesfully added to the database';
} else {
echo 'Query failed: ';
print_r( $pdo->errorInfo() );
echo '<br />';
}
}
}
check query:
PDOStatement Object ( [queryString] => INSERT INTO tth_user_accounts (user_login, user_pass, user_email, user_registered, user_firstname, user_lastname ) VALUES ("John Smith", "4\/\/350M3 P4sS\/\/0r|)", "john.smith#greatmail.com", "2013-04-11 11:18:58", "John", "Smith") )
Query failed: Array ( [0] => 00000 [1] => [2] => )
Tried it with a %25 and %50, both don't work. Keep getting the 00000 error which is supposed to lead to victory (success, though for me it still fails, nothing in the DB)
I'd do it manually if I had the time but this won't be a one-time event so I need a solution to this issue. Is there a good way to split up the query into batches (and how?) that would allow this to be repeated and queries to be executed one after the other? I've been looking at a whole bunch of questions on SO (and elsewhere) already and can't find one that suits my needs.
UPDATE - has been answered, need a small modification as shown below:
if (!( $i % 25 === 0 )){
if(!( $i == ( count( $users ) - 1 ))){
$sql = $sql . ', ';
}
}
You're mixing PDO with mysql_ functions. Pick one and follow the respective library's error handling.
It would also be beneficial print out your $sql to yourself to see if it's formatted correctly. Additionally, if you're handling POSTed data, you will want to use prepared statements,
You might need to increase the max_allowed_packet value which defaults to 1Mb. If you want to split up the query in batches you can do so using the modulus operator.
$base = 'INSERT INTO ...';
$sql = '';
while( $i < count( $users ) ){
$sql = $sql . ' ("' ... //etc.
if ($i % 50 === 0) {
//execute $base + $qry here
$sql = ''; //Re-init query string
}
}
if (strlen($qry)>0) { // Execute remainder, if any
//execute $base + $query here
}
Or as an array (described here):
$base = 'INSERT INTO ...';
$sql = array();
while( $i < count( $users ) ){
$sql[] = ' ("' ... //etc.
if ($i % 50 === 0) {
//execute $base + implode(',', $sql) here
$sql = array(); //Re-init query string
}
}
if (sizeof($qry)>0) { // Execute remainder, if any
//execute $base + implode(',', $sql) here
}
Also please make sure you're using prepared statements correctly so you're not vulnerable to SQL injections.
Finally: you might need to enable error reporting so failures won't be silent; if they're still silent after enabling error reporting (e.g. error_reporting(-1);) you might need to set MySQL to strict mode (not sure if that will help). If they still fail silently file a bugreport.
Edit
Oh, I missed the fact that you're mixing mysql_ and PDO; that will probably be the reason why you're not seeing any errors... D'uh. Go read the manual on PDO error handling.
#Downvoters: If you're downvoting at least have the decency to leave a comment on why.
Is there a reason why you want to do it as one single statement? Why not just iterate through the users and insert each one with something like:
while($i < count( $users )) {
$sql = 'INSERT INTO tth_user_accounts (user_login, user_pass, user_email, user_registered, user_firstname, user_lastname ) VALUES ';
$sql = $sql + "(";
$sql = $sql + "'" . $users[$i]['user_login'] . '",';
$sql = $sql + "'" . $users[$i]['user_pass'] . '",';
$sql = $sql + "'" . $users[$i]['user_email'] . '",';
$sql = $sql + "'" . $users[$i]['user_registered'] . '",';
$sql = $sql + "'" . $users[$i]['meta_value']['first_name'] . '",';
$sql = $sql + "'" . $users[$i]['meta_value']['last_name'] . '"';
$sql = $sql + ")";
$query = $pdo->prepare( $sql );
if( $query->execute() ){
echo 'User succesfully added to the database';
} else {
die ("Query failed: " . $pdo->errorInfo());
}
}
Even better is to prepare the statement once and then bind the parameters. As pointed out by RobIII, you can introduce SQL injection vulnerabilities by building up your SQL statements as strings so instead, you could do something like:
$sql = 'INSERT INTO tth_user_accounts (user_login, user_pass, user_email, user_registered, user_firstname, user_lastname) ';
$sql += ' VALUES (:user_login, :user_pass, :user_email, :user_registered, :user_firstname, :user_lastname)';
$query = $pdo->prepare( $sql );
while ($i < count($users)) {
$query->bindParam(":user_login", $users[$i]['user_login']);
$query->bindParam(":user_pass", $users[$i]['user_pass']);
$query->bindParam(":user_email", $users[$i]['user_email']);
$query->bindParam(":user_registered", $users[$i]['user_registered']);
$query->bindParam(":user_firstname", $users[$i]['user_firstname']);
$query->bindParam(":user_lastname", $users[$i]['user_lastname']);
if( $query->execute() ){
echo 'User succesfully added to the database';
} else {
die ("Query failed: " . $pdo->errorInfo());
}
}
Another issue you may be encountering is a simple fact that the user's name / information may contain invalid characters and killing your command. Take for instance a person's last name is "O'Maley". When building your string of values and wrapping the name with hard quotes, you would get
'O'Maley'
I ran into something similar to this about 8 years ago and had to validate certain values within the data. Once you find it, you'll know how to correct each respective value, then go back to batch mode.
Also, consider someone supplying a bogus value of a bogus / bad value of "--" which indicates rest of line is a comment... or even a ";" to indicate end of statement. That is most likely what you are running into.
As RobIII responded, and I was too quick in my response, don't test one at a time, but query the data an look at it for such anomolies / bad escape posts in the data. Fix what you need to BEFORE trying the insert.

how can I avoid to many OR statements in a mysql query?

I am writing a code, where I have to produce a query with many OR statements, and I think there is a more comfortable way to this than:
foreach ($plz as &$value) {
if (empty($i)) {
$query = "WHERE plz='$value'";
} else {
$query = "$query OR plz='$value'";
}
$i++;
}
$sql = mysql_query("SELECT * FROM table $query");
while ($data = mysql_fetch_array($sql)) {
//do something
}
If you have multiple values a column may take, just connect them using the IN operator:
Instead of writing
... WHERE col=1 OR col=2 OR col=3
just write
... WHERE col IN (1,2,3)
To collect all entries in PHP, use an array and implode() later on:
// collecting values
$vals = array();
$vals[] = 1;
$vals[] = 2;
// ...
// add them to your query
$query .= ' WHERE col IN ( ' . implode( ',', $vals ) . ')';
// execute the query ...
In case your values are not integer, but need to be enclosed in apostrophes within the query, insert them that way into the array in the first place:
$vals[] = "'my string value'";
You're looking for ;
SELECT * FROM table WHERE plz in ('value1', 'value2', 'value3')
Be aware of SQL injections...
If the column plz is INT type, and all $plz are also integers, then:
$query = 'WHERE plz IN( ' . implode(',', $plz) . ')';
would work. Otherwise, trying this might work(not tested):
$query = 'WHERE plz IN( \'' . implode("','", $plz) . '\')';

Categories