How to get inner array value using PHP without key - php

In my php file I get string which looks like [["category_id","=","3"],["price","<","40"]] from Android. I will use that for select query. But how can I get each item from it?
Here my code:
<?php
$filter = $_POST[‘filter’];
$filter_text = “”;
foreach($filter as $filter_item){
foreach($filter_item as list($column, $equality, $value)){
$filter_text .= "product." . $column . $equality. "'" . $value . "' AND ";
}
}
...
?>

Looks like you're constructing a WHERE clause to an SQL query. Here's a clue on how to build the string, but you should be using prepared statements instead of injecting user input directly into your query! Also, you don't actually need to nest a foreach, as is demonstrated below. See comments for explanation.
To use prepared statements (and please do!), you can substitute the variables in the ANDed conditions with "product.? ? ?";. Then use PDOStatement::bindValue(). Other flavors of parameter binding are available.
<?php
// Your input array.
$filters = [["category_id","=","3"],["price","<","40"]];
// Your question is unclear on this, but if your $_POST['filter'] is not actually an array, but a JSON string, then you may try this:
$filters = json_decode($_POST['filter'], TRUE); // TRUE to decode to associative array.
// $where holds each WHERE condition, so you can perform a clean string glue with implode().
// In your current string concatenation, additional conditions are required, or the query
// will fail due to an errant trailing AND if none are following.
$where = [];
// $filterText will hold this part of your WHERE clause. Consider renaming it to something
// more descriptive, like $whereSql.
$filterText = '';
foreach ($filters as [$key, $comparisonOperator, $value])
$where[] = "product.$key $comparisonOperator $value";
// Glue each condition with as many ANDs are necessary.
$filterText .= implode(' AND ', $where);
echo $filterText;
// Outputs: product.category_id = 3 AND product.price < 40
UPDATE: Your question is a little unclear on this, so I've added something that you can try. This updated answer now assumes your input isn't actually an array, but a JSON string.

You can use implode and some looping
$filter = isset($_POST['filter']) ? $_POST['filter'] : '';
$arr = json_decode($filter,true);
foreach($arr as $k => $v) {
$query['q'][] = [
'column' => 'product.' . $v[0],
'equality' => $v[1],
'value' => $v[2]
];
}
$filter = array();
foreach($query as $q) {
foreach($q as $k) {
$filter[]= sprintf("%s %s %s", $k['column'],$k['equality'],$k['value']);
}
}
$query = implode(' AND ',$filter);
echo $query;
// product.category_id = 3 AND product.price < 40
Warning: You are wide open to SQL Injections and should use parameterized prepared statements instead of manually building your queries. They are provided by PDO or by MySQLi. Never trust any kind of input! Even when your queries are executed only by trusted users, you are still in risk of corrupting your data. Escaping is not enough!
-Dharman

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
}

SQL Search query error in Laravel 5.3

I am trying to build a search query. I got following error, It seems syntax error of sql.
SQLSTATE[HY093]: Invalid parameter number (SQL: select * from products
where styles = Abstract , Abstract and subject = ? )
Why this error occurred ?
How to figure it out ?
My code as follows
if (isset($request->search)) {
//GET ALL INPUT FROM THE REQUEST
$query_strings = $request->all();
//PULL OUT ANY EMPTY FIELD FROM THE REQUEST
$filtered_array = array_filter($request->all());
//remove the last item
array_pop($filtered_array);
//BUILD A QUERY
$sql = array();
$values = array();
$x = 1;
foreach ( $filtered_array as $key =>$value ) {
if($x < count($filtered_array)){
$sql[]=" $key = ? and ";
$values[] =" $value , ";
} else {
$sql[]=" $key = ? ";
$values[] =" $value ";
}
$x++;
}
$fields = join(' ', $sql);
$v = join(' ',$values);
dd( \DB::select("select * from products where {$fields} ", [$v]));
}
When you're passing some values, you should add ? placeholder:
\DB::select("select * from products where ?", [$value]));
This is a bit of a stretch and I doubt it will work as is on the first try. But I really suggest you try and make use of the Laravel's query builder.
This code assumes you're passing 'products' table column names as names of GET or POST parameters and values you want to query by as values. For example:
url.com?price=200&size=2
Where 'price' and 'size' are column names of 'products' table.
Code:
// Check if request has 'search' parameter
if($request->has('search')) {
// $filtered_array now has all parameters that were passed to the controller
$filtered_array = $request->all();
// Start a query on table 'products'. Laravel style (more: https://laravel.com/docs/5.3/queries)
$query = \DB::table('products');
// For each parameter passed to the controller, we make "and where products.$key = $value" statement in the query
foreach($filtered_array as $key => $value) {
$query->where($key, '=', $value);
}
// Tell the query builder to get the results and save it to $results variable
$results = $query->get();
}
This will undoubtedly cause a lot of errors as anyone can send anything as GET/POST parameters and query by that (which will throw SQL error that the column doesn't exist).
You should change the $filtered_array = $request->all() to:
$filtered_array = $request->only(['id', 'price', 'size']);
This way you will store only the parameters you specify in the ->only(array) in the $filtered_array and ignore all the others. So you should replace 'id', 'price' and 'size' with all the columns of the 'products' table you wish to query by.

Looping through a variable array and inserting using bind_param

Im trying to loop through several arrays to insert data into a mysql database. And Im trying to bind the data so that I can loop through it. There can be a various number of columns to which data is bound.
It appears that the data Im binding is not being processed as expected and the insert ultimately fails.
I have a columns array that stores the column names and data types. I also have a values array that stores the values that are to be inserted. Sample data:
$colArr = array (
array('i', 'ID'),
array('s', 'Date')
);
$valArr = array(
array(1, 'now()'),
array(2, 'now()'),
);
//I create my type and query strings as well as the array referencing the columns for binding.
$valStrForQry = rtrim(str_repeat('?, ', count($v['colArr'])), ', '); //result: '?, ?'
$params = array();
$colsForQry = '';
$typeStr = '';
$cntr = 0;
foreach ($colArr as $cols) {
$colsForQry .= $cols[1] . ', ';
$typeStr .= $cols[0];
$params[] = &$valArr[$cntr][1];
$cntr++;
}
$colsForQry = rtrim($colsForQry, ', '); //result: 'ID, Date'
$qry = 'INSERT INTO table (' . $colsForQry . ') VALUES (' . $valStrForQry . ')';
$stmt = $mysqli->prepare($qry);
//Bind the parameters.
call_user_func_array(array($stmt, 'bind_param'), array_merge(array($typeStr), $params));
//Loop through the values array, assign them using eval, and execute the statement. Im open to suggestions if theres a better way to do this.
foreach ($valArr as $vals) {
$cntr = 0;
foreach ($colArr as $c) {
eval('$' . $c[1] . ' = ' . $vals[$cntr] . ';');
$cntr++;
}
if ($stmt->execute() === FALSE) {
//show $stmt->error for this iteration
} else {
//show success for this iteration
}
}
The first iteration results in a successful insertion of incorrect data. That is, the inserted ID is 0, not 1, and no other info is inserted. The second iteration (and all consecutive ones) results in the following error message: Duplicate entry '0' for key 'PRIMARY'
What am I doing wrong here, is it the eval or something else? Im not sure how to figure this one out.
Instead of continuing to try to get the existing code working, I'm going to suggest a KISS starting point, without the prepare(), the eval(), or the bind_param().
$cols = ['ID', 'Date'];
$vals = [
[1, '\'now()\''],
[2, '\'now()\''],
];
foreach ($vals as $val)
{
$sql = 'INSERT INTO table (' . implode($cols, ', ') . ') VALUES (' . implode($val, ', ') . ')';
// exec here
}
To make this a bit safer, you'll probably want to escape all the values before the implode, or before/as they are put into the array you're working with. The existing code is, IMHO, trying to be too "clever" to do something so simple.
Alternately, you may want to consider switching to using the PDO library instead of mysqli. PDO supports binding of named parameters on a per-parameter basis, which could be done in a loop without the eval().
Someone else may get the provided "clever" solution working instead of course.

Loop through an array to create an SQL Query

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) . "')";

Passing array to like active record in codeigniter

I would like to pass array to like clause of active record in codeignter for that i have written the following:
$array_string = smart,intelligent,awesome;
$array_like = explode(',',$array_string);
and in model:
$this->db->like('topic',array($array_like));
but I am getting
Severity: Notice
Message: Array to string conversion
Any help or suggestion would be a great help.
See updated question here:
join not giving required result when using or_like codeigniter
You can't just pass an array to the like() function, it has to be a string. You need to apply a like clause for each of the keywords in the string.
You need to use or_like to match a record to either of the keywords, however, because if you just use like() each time, it will need to match all of the keywords due to the query being like LIKE "%smart" AND LIKE "%intelligent" etc. and that isn't what you require.
$array_string = "smart,intelligent,awesome";
$array_like = explode(',', $array_string);
foreach($array_like as $key => $value) {
if($key == 0) {
$this->db->like('topic', $value);
} else {
$this->db->or_like('topic', $value);
}
}
Try this way to avoid your problem of the rest of the where statement being ignored.
$array_string = "smart,intelligent,awesome";
$array_like = explode(',', $array_string);
$like_statements = array();
foreach($array_like as $value) {
$like_statements[] = "topic LIKE '%" . $value . "%'";
}
$like_string = "(" . implode(' OR ', $like_statements) . ")";
The value of $like_string will be (topic LIKE '%smart%' OR topic LIKE '%intelligent%' OR topic LIKE '%awesome%')
You can then use $like_string in the following way with ActiveRecord:
$this->db->where($like_string, FALSE);
Assuming you are using Codeigniter 2.1.4, as you can read in CodeIgniter active record documentation, you cannot pass the second argument as array in like function. You need to pass a string as argument.
$array_string = "smart,intelligent,awesome";
$array_like = explode(',', $array_string);
foreach ($array_like as $like)
{
$this->db->like('topic', $like);
}

Categories