This is mysql table structure
item_id
StockNo
SizeCd
1
12003
UNIT
2
12007
JOGO
3
12008
PACOTE
4
12033
JOGO
5
12034
JOGO
6
12038
UNIT
I'm using plugin called DevExtreme for remote data grid. It's API request looks like below.
{
from: get_data
skip: 0
take: 50
requireTotalCount: true
filter: [["SizeCd","=","UNIT"],"or",["SizeCd","=","JOGO"]]
}
Where filter is what I need help with. What I want is I want to convert this string into MySQL where condition syntax.
I tried using php functions like array_merge join but couldn't get it to work. Sometimes it place quotes to both sides or sometimes quotes were missing. It should be like field name without quotes and value with quotes. Like in mysql where syntax.
Sorry for bad formatting and grammar mistakes.
For this array:
$array = [["SizeCd","=","UNIT"],"or",["SizeCd","=","JOGO"],"or",["SizeCd","=","PACOTE"]];
For PDO
You can use the following functions:
function arrayToQuery(string $tableName, array $array) : string
{
$select = "SELECT * FROM `{$tableName}` WHERE ";
foreach($array as $item) {
if(is_array($item)) {
$select .= "`{$item[0]}` {$item[1]} ?";
} else {
$select .= " {$item} ";
}
}
return $select;
}
function arrayToParams(array $array) : array
{
$return = [];
foreach($array as $item) {
if(is_array($item)) {
$return[] = $item[2];
}
}
return $return;
}
var_dump(
arrayToQuery("x", $array),
arrayToParams($array)
);
Output:
string(66) "SELECT * FROM `x` WHERE `SizeCd` = ? or `SizeCd` = ? or `SizeCd` = ?"
array(3) {
[0]=>
string(4) "UNIT"
[1]=>
string(4) "JOGO"
[2]=>
string(6) "PACOTE"
}
Example using PDO
$conn = /* your conn object */;
$sql = arrayToQuery("your_table_name", $array);
$stmt = $conn->prepare($sql);
$stmt->execute(arrayToParams($array));
Update: For Mysqli
For mysqli you can use the following function:
function arrayToQueryMysqli($mysqli, string $table, array $array) : string
{
$select = "SELECT * FROM `{$table}` WHERE ";
foreach($array as $item) {
if(is_array($item)) {
$select .= "`{$item[0]}` {$item[1]} '" . $mysqli->real_escape_string($item[2]) . "'";
} else {
$select .= " {$item} ";
}
}
return $select;
}
$mysqli = new mysqli(/* Your settings */);
$query = arrayToQueryMysqli($mysqli, "tablename", $array);
$result = $mysqli->query($query);
var_dump($result);
Related
Here is my function.
I want to simplify this function.
Any one help me please?
public function showData($table,$fields,$values)
{
$first = true;
$whereClause=null;
foreach($fields as $key => $value)
{
if($first)
{
$whereClause .= " WHERE $value = '$values[$key]'";
$first = false;
}
else
{
$whereClause .= " AND $value = '$values[$key]'";
}
}
$sql = "SELECT * FROM $table $whereClause";
$q = $this->conn->prepare($sql) or die("failed!");
$q->execute();
while ($r = $q->fetch(PDO::FETCH_ASSOC))
{
$data[] = $r;
}
return $data;
}
foreach($ob->showData($tablenm,$field,$val) as $roleval)
{
//Do Something
}
Any other way to simplify this function.
Help me please.
public function query($sql, $params = NULL)
{
$stmt = $this->conn->prepare($sql);
$stmt->execute($params)
return $stmt;
}
$data = $ob->query("SELECT * FROM table WHERE foo = ? AND bar = ?", [$foo, $bar]);
foreach($data as $roleval)
{
//Do Something
}
This function is way more simpler, way more powerful and way more flexible than yours. Put aside that yours is essentially and irrecoverably prone to SQL injection, just mocking a prepared statement but not using it really.
You have to understand that keywords in SQL serve for the great purpose of readability, makes whole statement readable and unambiguous, comprehensible by the every programmer in the world. And so you can tell that your initial idea to save yourself typing of SELECT or WHERE turns to be not that brilliant.
Besides, PDO supports dozens of different return formats, while you are limiting yourself with only one.
You may read further in my article Your first database wrapper's childhood diseases
function showData($table, $fields, $values) {
if(!(is_array($fields) && is_array($values) ) || count($fields) !== count($values))
throw new Exception('Arguments error: "fields" and "values" must be arrays with equal number of elements.');
foreach ($fields as $key => &$field)
$field = '`' . str_replace('`', '``', $field) . '` = ' . $this->conn->quote($values[$key]);
return 'SELECT * FROM `' . str_replace('`', '``', $table) . (empty($fields) ? '`' : '` WHERE ' . implode(' AND ', $fields)) . ';';
}
test case:
echo showData('table`name', ['col`1', 'col\`2', 'col\\`3'], ["Tom's cat 1", "Tom's cat 2", "Tom's cat 3"]);
#output: SELECT * FROM `table``name` WHERE `col``1` = 'Tom\'s cat 1' AND `col\``2` = 'Tom\'s cat 2' AND `col\``3` = 'Tom\'s cat 3';
Of course you will execute the SQL instead of returning it as test output.
I build SQL query with a method and then return it and use it.
$query = $this->buildSearchQuery($searchParams);
return $this->db->query($query);
Unfortunately this throw me an error:
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an
error in your SQL syntax; check the manual that corresponds to your
MySQL server version for the right syntax to use near ''SELECT * FROM
candidates WHERE firstname = ? AND surname = ?','Dante', 'Hickman' at
line 1
I was searching for it because this looks like SQL syntax fail of previous script which build query so I did simple thing I dump this $query before I used it.
Dump return this:
"'SELECT * FROM candidates WHERE firstname = ? AND surname = ?','Dante', 'Hickman'" (81)
Which is correctly, string with 81 chars.
After this, I try to put this to original query instead of variabile and it looks like this:
return $this->db->query('SELECT * FROM candidates WHERE firstname = ? AND surname = ?','Dante', 'Hickman');
This secod script run correcty so it looks query is build correctly, but still error. I am missing something?
I hope for any advise which can help me solve this problem.
p.s. Syntax of that query is from nette framework but system should be the same.
EDIT:
adding buildSearchQuery()
function buildSearchQuery($searchParams)
{
$column = "";
$values = "";
$col = "";
$i=0;
// Trim to make sure user doesn't enter space there
if((trim($searchParams->firstname)))
{
$column .= "firstname,";
$i++;
}
if((trim($searchParams->surname)))
{
$column .= "surname,";
$i++;
}
if((trim($searchParams->specialization)))
{
$column .= "specialization,";
$i++;
}
if($searchParams->english !== NULL)
{
$column .= "english,";
$i++;
}
if($searchParams->german !== NULL)
{
$column .= "german,";
$i++;
}
if($searchParams->russian !== NULL)
{
$column .= "russian,";
$i++;
}
if($searchParams->french !== NULL)
{
$column .= "french,";
$i++;
}
if($searchParams->school !== NULL)
{
$column .= "school,";
$i++;
}
if((trim($searchParams->registrationDate)))
{
$column .= "registrationDate";
$i++;
}
if($i > 0)
{
// If number of columns is bigger then 0 (if user fill atleast one input)
$columns = explode(",", $column);
// Create list of values for query (name of columns and values)
foreach($columns as $c)
{
if (isset($searchParams->$c)) {
$values .= "'".$searchParams->{$c}."', ";
$col .= $c." = ? AND ";
}
}
// Remove last "," and space
$values = substr_replace($values, "", -2);
$col = substr_replace($col, "", -5);
$query = $col."',".$values;
$query = "'SELECT * FROM candidates WHERE ".$query;
//$query = substr($query, 0, -1); //remove last char ( ' in this case)
return $query;
}
else
{
$query = "SELECT * FROM candidates";
return $query;
}
}
The comments above are correct, you are passing a string as the only argument, instead of multiple arguments query expects.
One possible solution is creating an array and calling the method with array items as arguments (e.g. using call_user_func_array). You can however do better.
Nette\Database is quite powerful and it can build the query for you. When you pass an associative array like ["column1" => "value1", "column2" => "value2"] as the only argument of where method, it will create corresponding WHERE column1 = 'value1' AND column2 = 'value2' clause. And of course it will securely escape the values to prevent SQL injection.
You can, therefore, simplify your code into something like following:
$columns = ["firstname", "surname", "specialization", "english", "german", "russian", "french", "school", "registrationDate"];
$conditions = [];
foreach ($columns as $c) {
if (isset($searchParams->$c) && trim($searchParams->$c) !== "") {
$conditions[$c] = $searchParams->{$c};
}
}
return $this->db->table('candidates')->where($conditions);
No if–else statement is needed as when the array is empty, NDB correctly doesn’t append the WHERE clause.
Is there a way to count the number of variables in an object?
I have a object that is created dynamically, and I want to add each of the properties to a query to update these properties into the database.
The variable $oProperties is an object:
public function update_model ($id, $oProperties)
{
$SQL = "UPDATE `table` SET ";
$count = 0;
foreach($oProperties as $property=>$value)
{
$count++;
$SQL .= strtolower($property)." = '".$value."'";
if($count !== $oProperties::count()) {$SQL .= ", ";}
}
$SQL .= " WHERE id='".$id."';";
}
I need to know the amount of properties in the object to know when to stop adding the comma to the query.
You can use get_object_vars:
class foo {
private $a;
public $b = 1;
public $c;
private $d;
static $e;
}
$test = new foo;
var_dump(get_object_vars($test));
Result:
array(2) {
["b"]=>
int(1)
["c"]=>
NULL
}
or if your object has type ArrayObject, you can use count method:
$arrayobj = new ArrayObject(array('first','second','third'));
var_dump($arrayobj->count());
But I think will be better use something like that:
public function update_model ($id, $oProperties)
{
$SQL = "UPDATE `table` SET ";
$properties = [];
foreach($oProperties as $property=>$value)
{
$properties[] = strtolower($property)." = '".$value."'";
}
$SQL .= implode(', ', $properties);
$SQL .= " WHERE id='{$id}';";
return $SQL;
}
$arrayObj = (array)($oProperties);
print_r(count($arrayObj));
I have the following PHP, that needs to take a list of article ids and run it through a function to get them all from the database.
I am very much a beginner in PHP so far I have:
list($slide1, $slide2, $slide3, $slide4, $slide5) = explode(" ", $params->get('id'));
$a = array(
$item => $slide1,
"two" => $slide2,
// "three" => $slide3,
// "four" => $slide4,
// "five" => $slide5
);
foreach ($a as $k) {
$args['id'] = $k;
$item = ModArticleSlider::getArticles($args);
}
and the class:
class ModArticleSlider {
public function getArticles($args){
$db = &JFactory::getDBO();
$item = "";
$id = $args['id'];
if($id > 0){
$query = "select * ";
$query .= "FROM #__content WHERE id =".$id." AND state=1 " ;
//echo $query;
$db->setQuery($query);
$item = $db->loadObject();
}
return $item;
}
}
How would i get it so that instead of specifying $item to have it almost dynamic so that i can get all the selected articles and place them in different variables.. or would i have to place them in a array?
Any Advice/Answers Greatly Appreciated.
Another approach, and possibly more performant as it would require only one database query (versus one for each article), would be to use http://docs.joomla.org/JDatabase::loadObjectList
So, you could instead pass an array of your articles ids as $ids to your class like:
public function getArticles($ids){
if ($ids) {
$db = JFactory::getDbo();
$query = "SELECT * FROM #__content WHERE id IN (" . implode(',', $id) . ") AND state=1 " ;
$db->setQuery($query);
$rows = $db->loadObjectList();
return $rows;
}
return FALSE;
}
$rows is returned as an array so you can then process it as needed.
i am also beginner of PHP.
Please Try this:
foreach ($a as $k => $val) {
$args = $val;
$item[] = ModArticleSlider::getArticles($args);
var_dump($item);
}
in your class
simply
$id = $args;
i hope it will help you!
Store the articles into an array, you can try this:
$slides = explode(" ", $params->get('id'));
$articleArray = array();
foreach($slides as $slide) {
$articleArray[] = ModArticleSlider::getArticles($slide);
}
var_dump($articleArray);
So I'm trying to create a function that generates a SQL query string based on a multi dimensional array.
Example:
function createQueryString($arrayToSelect, $table, $conditionalArray) {
$queryStr = "SELECT ".implode(", ", $arrayToSelect)." FROM ".$table." WHERE ";
$queryStr = $queryStr.implode(" AND ",$conditionalArray); /*NEED HELP HERE*/
return $queryStr;
}
$columnsToSelect = array('ID','username');
$table = 'table';
$conditions = array('lastname'=>'doe','zipcode'=>'12345');
echo createQueryString($columnsToSelect, $table, $conditions); /*will result in incorrect SQL syntax*/
as you can see I need help with the 3rd line as it's currently printing
SELECT ID, username FROM table WHERE
lastname AND zipcode
but it should be printing
SELECT ID, username FROM table WHERE
lastname = 'doe' AND zipcode = '12345'
You're not actually imploding a multidimensional array. $conditions is an associative array.
Just use a foreach loop inside your function createQueryString(). Something like this should work, note it's untested.:
$terms = count($conditionalArray);
foreach ($conditionalArray as $field => $value)
{
$terms--;
$queryStr .= $field . ' = ' . $value;
if ($terms)
{
$queryStr .= ' AND ';
}
}
Note: To prevent SQL injection, the values should be escaped and/or quoted as appropriate/necessary for the DB employed. Don't just copy and paste; think!
function implodeItem(&$item, $key) // Note the &$item
{
$item = $key . "=" . $item;
}
[...]
$conditionals = array(
"foo" => "bar"
);
array_walk($conditionals, "implodeItem");
implode(' AND ', $conditionals);
Untested, but something like this should work. This way you can also check if $item is an array and use IN for those cases.
You will have to write another function to process the $conditionalArray, i.e. processing the $key => $value and handling the types, e.g. applying quotes if they're string.
Are you just dealing with = condition? What about LIKE, <, >?
Forgive me if its not too sexy !
$data = array('name'=>'xzy',
'zip'=>'3432',
'city'=>'NYK',
'state'=>'Alaska');
$x=preg_replace('/^(.*)$/e', ' "$1=\'". $data["$1"]."\'" ',array_flip($data));
$x=implode(' AND ' , $x);
So the output will be sth like :
name='xzy' AND zip='3432' AND city='NYK' AND state='Alaska'
I'd advise against automated conditionals creation.
Your case is too local, while there can be many other operators - LIKE, IN, BETWEEN, <, > etc.
Some logic including several ANDs and ORs.
The best way is manual way.
I am always doing such things this way
if (!empty($_GET['rooms'])) $w[]="rooms='".mesc($_GET['rooms'])."'";
if (!empty($_GET['space'])) $w[]="space='".mesc($_GET['space'])."'";
if (!empty($_GET['max_price'])) $w[]="price < '".mesc($_GET['max_price'])."'";
Though if you still want it with this simple array, just iterate it using
foreach ($conditions as $fieldname => $value)...
and then combine these variables in the way you need. you have 2 options: make another array of this with field='value' pairs and then implode it, or just concatenate, and substr trailing AND at the end.
I use a variation of this:
function implode_assoc($glue,$sep,$arr)
{
if (empty($glue)) {$glue='; ';}
if (empty($sep)) {$sep=' = ';}
if (is_array($arr))
{
foreach ($arr as $k=>$v)
{
$str .= $k.$sep.$v.$glue;
}
return $str;
} else {
return false;
}
};
It's rough but works.
Here is a working version:
//use: implode_assoc($v,"="," / ")
//changed: argument order, when passing to function, and in function
//output: $_FILES array ... name=order_btn.jpg / type=image/jpeg / tmp_name=G:\wamp\tmp\phpBDC9.tmp / error=0 / size=0 /
function implode_assoc($arr,$glue,$sep){
$str = '';
if (empty($glue)) {$glue='; ';}
if (empty($sep)) {$sep=' = ';}
if (is_array($arr))
{
foreach ($arr as $key=>$value)
{
$str .= $key.$glue.$value.$sep;
}
return $str;
} else {
return false;
}
}
I know this is for the case of a pdo mysql type.. but what i do is build pdo wrapper methods, and in this case i do this function that helps to build the string, since we work with keys, there is no possible way to mysql inject, since i know the keys i define / accept manually.
imagine this data:
$data=array(
"name"=>$_GET["name"],
"email"=>$_GET["email"]
);
you defined utils methods...
public static function serialize_type($obj,$mode){
$d2="";
if($mode=="insert"){
$d2.=" (".implode(",",array_keys($obj)).") ";
$d2.=" VALUES(";
foreach ($obj as $key=>$item){$d2.=":".$key.",";}
$d2=rtrim($d2,",").")";}
if($mode=="update"){
foreach ($obj as $key=>$item){$d2.=$key."=:".$key.",";}
}
return rtrim($d2,",");
}
then the query bind array builder ( i could use direct array reference but lets simplify):
public static function bind_build($array){
$query_array=$array;
foreach ($query_array as $key => $value) { $query_array[":".$key] = $query_array[$key]; unset($query_array[$key]); } //auto prepair array for PDO
return $query_array; }
then you execute...
$query ="insert into table_x ".self::serialize_type( $data, "insert" );
$me->statement = #$me->dbh->prepare( $query );
$me->result=$me->statement->execute( self::bind_build($data) );
You could also go for an update easy with...
$query ="update table_x set ".self::serialize_type( $data, "update" )." where id=:id";
$me->statement = #$me->dbh->prepare( $query );
$data["id"]="123"; //add the id
$me->result=$me->statement->execute( self::bind_build($data) );
But the most important part here is the serialize_type function
Try this
function GeraSQL($funcao, $tabela, $chave, $valor, $campos) {
$SQL = '';
if ($funcao == 'UPDATE') :
//Formata SQL UPDATE
$SQL = "UPDATE $tabela SET ";
foreach ($campos as $campo => $valor) :
$SQL .= "$campo = '$valor', ";
endforeach;
$SQL = substr($SQL, 0, -2);
$SQL .= " WHERE $chave = '$valor' ";
elseif ($funcao == 'INSERT') :
//Formata SQL INSERT
$SQL = "INSERT INTO $tabela ";
$SQL .= "(" . implode(", ", array_keys($campos) ) . ")";
$SQL .= " VALUES ('" . implode("', '", $campos) . "')";
endif;
return $SQL;
}
//Use
$data = array('NAME' => 'JOHN', 'EMAIL' => 'J#GMAIL.COM');
GeraSQL('INSERT', 'Customers', 'CustID', 1000, $data);