Loop through an array to create an SQL Query - php

I have an array like the following:
tod_house
tod_bung
tod_flat
tod_barnc
tod_farm
tod_small
tod_build
tod_devland
tod_farmland
If any of these have a value, I want to add it to an SQL query, if it doesnt, I ignore it.
Further, if one has a value it needs to be added as an AND and any subsequent ones need to be an OR (but there is no way of telling which is going to be the first to have a value!)
Ive used the following snippet to check on the first value and append the query as needed, but I dont want to copy-and-paste this 9 times; one for each of the items in the array.
$i = 0;
if (isset($_GET['tod_house'])){
if ($i == 0){
$i=1;
$query .= " AND ";
} else {
$query .= " OR ";
}
$query .= "tod_house = 1";
}
Is there a way to loop through the array changing the names so I only have to use this code once (please note that $_GET['tod_house'] on the first line and tod_house on the last line are not the same thing! - the first is the name of the checkbox that passes the value, and the second one is just a string to add to the query)
Solution
The answer is based heavily upon the accepted answer, but I will show exactly what worked in case anyone else stumbles across this question....
I didnt want the answer to be as suggested:
tod_bung = 1 AND (tod_barnc = 1 OR tod_small = 1)
rather I wanted it like:
AND (tod_bung = 1 OR tod_barnc = 1 OR tod_small = 1)
so it could be appended to an existing query. Therefore his answer has been altered to the following:
$qOR = array();
foreach ($list as $var) {
if (isset($_GET[$var])) {
$qOR[] = "$var = 1";
}
}
$qOR = implode(' OR ', $qOR);
$query .= " AND (" .$qOR . ")";
IE there is no need for two different arrays - just loop through as he suggests, if the value is set add it to the new qOR array, then implode with OR statements, surround with parenthesis, and append to the original query.
The only slight issue with this is that if only one item is set, the query looks like:
AND (tod_bung = 1)
There are parenthesis but no OR statements inside. Strictly speaking they arent needed, but im sure it wont alter the workings of it so no worries!!

$list = array('tod_house', 'tod_bung', 'tod_flat', 'tod_barnc', 'tod_farm', 'tod_small', 'tod_build', 'tod_devland', 'tod_farmland');
$qOR = array();
$qAND = array();
foreach ($list as $var) {
if (isset($_GET[$var])) {
if (!empty($qAND)) {
$qOR[] = "$var = 1";
} else {
$qAND[] = "$var = 1";
}
$values[] = $_GET[$var];
}
}
$qOR = implode(' OR ', $qOR);
if ($qOR != '') {
$qOR = '(' . $qOR . ')';
}
$qAND[] = $qOR;
$qAND = implode(' AND ', $qAND);
echo $qAND;
This will output something like tod_bung = 1 AND (tod_barnc = 1 OR tod_small = 1)

As the parameter passed to $_GET is a string, you should build an array of strings containing all the keys above, iterating it and passing the values like if (isset($_GET[$key])) { ...
You could then even take the key for appending to the SQL string.

Their are a lot of ways out their
$list = array('tod_house', 'tod_bung', 'tod_flat', 'tod_barnc', 'tod_farm', 'tod_small', 'tod_build', 'tod_devland', 'tod_farmland');
if($_GET){
$query = "";
foreach ($_GET as $key=>$value){
$query .= (! $query) ? " AND ":" OR ";
if(in_array($key,$list) && $value){
$query .= $key." = '".$value."'";
}
}
}
Sure you have to take care about XSS and SQL injection

If the array elements are tested on the same column you should use IN (...) rather than :
AND ( ... OR ... OR ... )
If the values are 1 or 0 this should do it :
// If you need to get the values.
$values = $_GET;
$tod = array();
foreach($values as $key => $value) {
// if you only want the ones with a key like 'tod_'
// otherwise remove if statement
if(strpos($key, 'tod_') !== FALSE) {
$tod[$key] = $value;
}
}
// If you already have the values.
$tod = array(
'tod_house' => 1,
'tod_bung' => 0,
'tod_flat' => 1,
'tod_barnc' => 0
);
// remove all array elements with a value of 0.
if(($key = array_search(0, $tod)) !== FALSE) {
unset($tod[$key]);
}
// discard values (only keep keys).
$tod = array_keys($tod);
// build query which returns : AND column IN ('tod_house','tod_flat')
$query = "AND column IN ('" . implode("','", $tod) . "')";

Related

Combine two if conditions

Im a new and just learning php. I have a data table with search boxes with this code.
$condition = '';
if(isset($_REQUEST['username']) and $_REQUEST['username']!="") {
$condition .= ' AND username LIKE "%'.$_REQUEST['username'].'%" ';
}
if(isset($_REQUEST['useremail']) and $_REQUEST['useremail']!=""){
$condition .= ' AND useremail LIKE "%'.$_REQUEST['useremail'].'%" ';
}
What I need is to search with both username AND useremail. I have attempted everything I know and spent a few hours searching for a solution but with no success.
You could write a complicated set of IF's with equals and not equals tests all over the place, but as the list of test gets bigger the IF's get almost impossible to maintain or understand. So it might be simpler to just build and array of things to AND in the query
$condition = '';
$and = []; #init the array
if(isset($_REQUEST['username']) and $_REQUEST['username']!="") {
$and[] = ['name' => 'username', 'value' => $_REQUEST['username'] ];
}
if(isset($_REQUEST['useremail']) and $_REQUEST['useremail']!=""){
$and[] = [''name' => 'useremail', 'value' => $_REQUEST['useremail'] ];
}
#now build the condition string
foreach ($and as $i => $andMe) {
if ( $i != 0 ){
// AND required here
$condition .= ' AND ';
}
$condition .= $andMe['name'] . ' = ' . $andMe['value'];
}
Also I have replaced the LIKE with an = as it seems more appropriate, I assume you dont ask people to enter something a bit like there user name and email, but in fact ask for the actual username or email
Of course that would still be susceptible to SQL Injection Attack So a better solution would be
#now build the condition string
foreach ($and as $i => $andMe) {
if ( $i != 0 ){
// AND required here
$condition .= ' AND ';
}
$condition .= $andMe['name'] . ' = ?';
}
And then prepare the query and use the value part to bind to the parameters.
Issue is you have leading AND in your query.
push condition to array then join conditions.
like that
$condition_array = [];
if(isset($_REQUEST['username']) and $_REQUEST['username']!="") {
$condition_array[] = 'username LIKE "%'.$_REQUEST['username'].'%" ';
}
if(isset($_REQUEST['useremail']) and $_REQUEST['useremail']!=""){
$condition_array[] = 'useremail LIKE "%'.$_REQUEST['useremail'].'%" ';
}
$condition = implode(" AND ",$condition_array);
You can create an array of all the keys that are to be searched. Then, create a new array and collect all conditions. Implode them in the end with AND as the glue. This way, query is made correctly without needing to add 100 different if conditions.
Use PDO objects to avoid SQL injection attacks. In the below snippet, if you ever need to add 1 more column for search, just add it in the below $keys array and rest works as usual without needing any further refactoring.
Snippet:
<?php
$keys = ['username', 'useremail'];
$conditions = [];
$placeholders = [];
foreach($keys as $key){
if(!empty($_REQUEST[ $key ])){
$conditions = " $key LIKE ?";
$placeholders[] = '%' . $_REQUEST[ $key ] . '%';
}
}
// you need to create $mysqli object here
if(count($conditions) === 0){
$stmt = $mysqli->prepare('select * from table');
$stmt->execute();
// rest of your code
}else{
$stmt = $mysqli->prepare('select * from table where '. implode(" AND ", $conditions));
$stmt->bind_param(str_repeat('s', count($placeholders)), ...$placeholders);
$stmt->execute();
// rest of your code
}

mysql: wp database update

So, I have a following js and php:
JS:
var names = jQuery('#name').val();
data : {'action':'AJAX' , name:names },
The #name values are "mike,sean,steve"
PHP:
global $wpdb;
$names = $_POST['name'];
$table = $wpdb->prefix . 'my_name';
$RSS_UPDATE = $wpdb->get_col("SELECT update_number FROM $table WHERE id_name IN ($names)");
//update_number are int (example: 0,3,1,2)
$name = explode(',', $names);
if ( $RSS_UPDATE ){
foreach ( $RSS_UPDATE as $RSS_SINGLE ){
$RSS_ROW_NEW = $RSS_SINGLE + 1;
$wpdb->update($table, array('update_number' => $RSS_ROW_NEW),array( 'id_name' => $name));
}
}
So, few things:
what I am trying to achieve:
With the input values, get corresponding update_number. Then increase each value by "1" and update the same column with the new value.
Errors
Unknown column 'Array' in 'where clause' for query SELECT update_number FROM wp_my_name WHERE id_name IN (Array)
Just in general, something is not right...
Can someone help me out?
Thank you.
EDIT:
Does this look right?
if(!empty($_POST['name'])) {
$names = $_POST['name']; //array
$table = $wpdb->prefix . 'rh_subs';
$query = "SELECT update_number FROM $table WHERE id_name = %s";
$RSS_UPDATE = $wpdb->get_results($wpdb->prepare($query, $names));
if(!empty($RSS_UPDATE)) {
foreach($RSS_UPDATE as $RSS_SINGLE) { // for each row
$RSS_ROW_NEW = $RSS_SINGLE->update_number + 1;
$wpdb->update($table, array('update_number' => $RSS_ROW_NEW),array('id_name' => $RSS_SINGLE->id_name));
}
}
}
Just what I've said in the comments in your earlier post, since you're taking multiple inputs, you'll need to use the WHERE IN clause.
The simple example would be like this:
$_POST['name']; // these are comma delimited string of names
// "mike,sean,steve"
So in essence, you'll need to construct them inside a WHERE IN clause like this:
WHERE id_name IN ('mike', 'sean', 'steve')
The unsafe and dirtiest way would be to just explode - put quotations on the strings - implode it back together with comma again:
$names = array_map(function($e){
return "'$e'";
}, explode(',', $test));
$names = implode(',', $names);
// 'mike','sean','steve' // SATISFIES WHERE IN CLAUSE
// BUT UNSAFE!
So in order to do this safely, use the wpdb prepared statements. (This could get you started).
if(!empty($_POST['name'])) {
$names = explode(',', $_POST['name']); // explode the comma delimited string into an array
$table = $wpdb->prefix . 'my_name';
$stringPlaceholders = implode(', ', array_fill(0, count($names), '%s')); // create placeholders for the query statement, this will generate
$statement = $wpdb->prepare("SELECT update_number, id_name FROM $table WHERE id_name IN ($stringPlaceholders)", $names); // create the statement using those placeholders
$RSS_UPDATE = $wpdb->get_results($statement); // execute
// fetch resuls
if(!empty($RSS_UPDATE)) {
foreach($RSS_UPDATE as $RSS_SINGLE) { // for each row
$RSS_ROW_NEW = $RSS_SINGLE->update_number + 1;
$wpdb->update($table, array('update_number' => $RSS_ROW_NEW),array('id_name' => $RSS_SINGLE->id_name));
}
}
}
Note: Of course you can get creative yourself. I think you could combine the UPDATE and WHERE IN clause so that you'll just execute all of this once.
First of all, It seems that $_POST['name'] returns an array.
You can view what exactly you are getting in $_POST['name'] by:
var_dump($_POST['name'], true);
Also For the id_name, if they are like these "mike,sean,steve" then you should do this for adding quotes for strings and the escaping issue so that they can be like this "'mike','sean','steve'" as you are using a WHERE IN clause:
$names = $_POST['name'];
if(!is_array($names)) $names = explode(",",$names);
$new_names = array();
foreach($names as $name){
$name = get_magic_quotes_gpc() ? stripslashes($name) : $name;
$new_names[] = "'".mysql_real_escape_string($name)."'";
}
$names = implode(",", $new_names);

How can I dynamically build a parameterized query for RedBeanPHP 4?

So I have some data coming in via POST from a form with a large number of checkboxes and I'm trying to find records in the database that match the options checked. There are four sets of checkboxes, each being sent as an array. Each set of checkboxes represents a single column in the database and the values from the checked boxes are stored as a comma-delimited string. The values I'm searching for will not necessarily be consecutive so rather than a single LIKE %value% I think I have to break it up into a series of LIKE statements joined with AND. Here's what I've got:
$query = "";
$i = 1;
$vals = [];
foreach($_POST["category"] as $val){
$query .= "category LIKE :cat".$i." AND ";
$vals[":cat".$i] = "%".$val."%";
$i++;
}
$i = 1;
foreach($_POST["player"] as $val){
$query .= "player LIKE :plyr".$i." AND ";
$vals[":plyr".$i] = "%".$val."%";
$i++;
}
$i = 1;
foreach($_POST["instrument"] as $val){
$query .= "instrument LIKE :inst".$i." AND ";
$vals[":inst".$i] = "%".$val."%";
$i++;
}
$i = 1;
foreach($_POST["material"] as $val){
$query .= "material LIKE :mat".$i." AND ";
$vals[":mat".$i] = "%".$val."%";
$i++;
}
$query = rtrim($query, " AND ");
$tubas = R::convertToBeans("tuba", R::getAll("SELECT * FROM tuba WHERE ".$query, $vals));
This does seem to work in my preliminary testing but is it the best way to do it? Will it be safe from SQL injection?
Thanks!
As long as you use parameterized queries (like you do), you should be safe from SQL injection. There are edge cases though, using UTF-7 PDO is vulnerable (I think redbean is based on PDO)
I would change the code to something like this, minimizes the foreach clutter.
$query = 'SELECT * FROM tuba';
$where = [];
$params = [];
$checkboxes = [
'category',
'player',
'instrument',
'material'
];
foreach ($checkboxes as $checkbox) {
if (!isset($_POST[$checkbox])) {
// no checkboxes of this type submitted, move on
continue;
}
foreach ($_POST[$checkbox] as $val) {
$where[] = $checkbox . ' LIKE ?';
$params[] = '%' . $val . '%';
}
}
$query .= ' WHERE ' . implode($where, ' AND ');
$tubas = R::convertToBeans('tuba', R::getAll($query, $params));

Extracting an array into dynamic variables

I am trying to be lazy (or smart): I have 7 checkboxes which correlate with 7 columns in a MySQL table.
The checkboxes are posted in an array:
$can = $_POST['can'];
I've created the following loop to dump the variables for the MySQL insert:
for($i=1;$i<8;$i++){
if($can[$i] == "on"){
${"jto_can".$i} = 'Y';
}
else{
${"jto_can".$i} = 'N';
}
}
print_r($jto_can1.$jto_can2.$jto_can3.$jto_can4.$jto_can5.$jto_can6.$jto_can7);
This correctly outputs:
YYNYYYY
However, when I attempt to use those variables in my MySQL update, it doesn't accept the changes.
mysqli_query($db, "UPDATE jto SET jto_can1 = '$jto_can1', jto_can2 = '$jto_can2', jto_can3 = '$jto_can3', jto_can4 = '$jto_can4', jto_can5 = '$jto_can5', jto_can6 = '$jto_can6', jto_can7 = '$jto_can7' WHERE jto_id = '$id'")or die(mysqli_error($db));
Can anyone explain why the print_r displays the variables whereas MySQL update does not?
Stick with the array, and form the query dynamically:
$sql = 'UPDATE jto SET ';
$cols = array();
foreach( range( 1, 7) as $i) {
$value = $_POST['can'][$i] == 'on' ? 'Y' : 'N'; // Error check here, $_POST['can'] might not exist or be an array
$cols[] = 'jto_can' . $i . ' = "' . $value . '"';
}
$sql .= implode( ', ', $cols) . ' WHERE jto_id = "' . $id . '"';
Now do a var_dump( $sql); to see your new SQL statement.
this is not a mysql problem. mysql will only see what you put into that string. e.g. dump out the query string BEFORE you do mysql_query. I'm guessing you're doing this query somewhere else and have run into scoping problems. And yes, this is lazy. No it's not "smart". you're just making MORE work for yourself. What's wrong with doing
INSERT ... VALUES jto_can1=$can[0], jto_can2=$can[1], etc...

SQL query and preg_match

Need your help with sql query and php.
I have to pieces of code here:
1.
$sql = "SELECT SUBSTR(n.`title`, 1,1) FROM node n WHERE n.`type` = 'type1'";
$results = db_query($sql);
while ($fields = db_fetch_array($results)) {
foreach($fields as $key => $value) {
echo $value;
}
}
The code above returns first letters of my article titles (table - node, columns - type, title) like this - NHDKFLF...
2.
if (preg_match ('/A/i', $string)) {
echo ('Contains letter A'); //
}
else {
echo ('Nothing'); //
}
And the second part checks if the string contains certain letters.
Now, the question is how to combine these two pieces of code? I mean how to pull data from DB and check if it has certain letters.
Thanks in advance.
Two options come to mind: Do what you're doing now, or re-write the SQL to do both at once.
Option 1:
$sql = "SELECT SUBSTR(n.`title`, 1,1) FROM node n WHERE n.`type` = 'type1'";
$results = db_query($sql);
while ($fields = db_fetch_array($results)) {
foreach($fields as $key => $value) {
if (preg_match ('/A/i', $value)) {
echo ('Contains letter A'); //
} else {
echo ('Nothing'); //
}
}
}
Option 2:
$sql = "SELECT SUBSTR(n.`title`, 1,1) FROM node n WHERE n.`type` = 'type1' AND SUBSTR(n.`title`, 1,1) = 'A' ";
Depending on the rest of the details of your project there is probably a better way to handle this.
Why wouldn't you just query for the ones you want?
... where SUBSTR(n.`title`, 1,1) = 'A' ...
If you must filter in your code, outside the query, then:
foreach($fields as $key => $value) {
if (preg_match ('/A/i', $value)) {
...
}

Categories