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.
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.
Hi so I have an insert statement which works well, but need to create a separate update function which uses array keys and array values, which would be quite like the insert function but updates.
I have this for my insert
$sql = "INSERT INTO $tablename (".implode(",", array_keys($DATA).")" . " DATA ('".implode("','",array_values($DATA))."')";
connect()->query($sql);
This is what I have for my update so far but am stuck with it,
<?php
function updatethis (array $id, array $values, $tablename)
{
$sql = "UPDATE $tablename SET (".implode(",", array_keys($DATA)).")" . " DATA ('".implode("','",array_values($DATA))."')";
dbconnect()->query($sql);
}
?>
Therefore I would like help on the update feature please .
So I am getting an error with the UPDATE syntax
This is the part i am struggling with, i cna give further explanation, but i must have put in the wrong syntax to update the database after i click edit on the index page it calls the function just the syntax is incorrect.
also its php to mySQL
index page for PHP updatee fucntion
{
$values = array();
$idValues = array($idColumn => $id);
foreach($_POST as $key => $value)
{
if(!empty($value) && ($value != "Submit"))
{
$values[$key] = $value;
}
}
$result = update($idValues, $values, $tableName);
}
Edit: Error I am getting
edit has not been successfull from below
if(isset($_POST['Submit']))
{
if($result>0)
{
echo 'Edit has been successful. Return to index page';
}
else
{
echo 'Edit has not been successful.';
}
}
My code
function updateAll(array $id, array $values, $tablename)
{
$sIDColumn = key($id);
$sIDValue = current($id);
$arrayValues = $values;
array_walk($values, function(&$value, $key){
$value = "{$key} = '{$value}'";
});
$sUpdate = implode(", ", array_values($values));
$sql = "UPDATE {$tablename} SET {$sUpdate} WHERE {$sIDColumn} = '{$sIDValue}'";
connect()->query($sql);
}
My aim: takes the input of the unique identifier of the row to be edited as an array of 1 then the value plus the name of the column representing the primary key, an array containing the values indexed by the column names as well as a string representing the table name useing array_keys and array_vaules like th insert but to update
You cannot UPDATE in the same way of INSERT. It should be like this :
$valueSets = array();
foreach($values as $key => $value) {
$valueSets[] = $key . " = '" . $value . "'";
}
$conditionSets = array();
foreach($id as $key => $value) {
$conditionSets[] = $key . " = '" . $value . "'";
}
$sql = "UPDATE $tablename SET ". join(",",$valueSets) . " WHERE " . join(" AND ", $conditionSets);
See details here http://dev.mysql.com/doc/refman/5.7/en/update.html
I believe the pattern you are using is incorrect?
UPDATE table SET (rows) DATA ('values');
I think updates look more like this:
UPDATE table SET row1 = 'value1', row2 = 'value2';
In which case, this may be closer to what you are looking for.
function updatethis(array $id, array $values, $tablename)
{
$sIDColumn = key($id);
$sIDValue = current($id);
$arrayValues = $values;
array_walk($values, function(&$value, $key){
$value = "{$key} = '{$value}'";
});
$sUpdate = implode(", ", array_values($values));
$sql = "UPDATE {$tablename} SET {$sUpdate} WHERE {$sIDColumn} = '{$sIDValue}'";
dbconnect()->query($sql);
}
Using it, I get this query:
$testArray = array(
"id" => 19,
"username" => "test"
);
updatethis(array("id" => 9), $testArray, "users");
UPDATE users SET id = '19', username = 'test' WHERE id = '9'
I hope this at least helps but when it comes to databases, I only know MySQL and it is possible you are using another language.
I think you can try something like this :
$champs : Array of fields to update
$valeurs : Array of value to update fields
$conditions : Array of conditions
protected function modify($table,$champs,$valeurs,$conditions){
$Requete = "UPDATE ".$table." SET ";
$nbChamps = count($champs);
$nbValeurs = count($valeurs);
if($nbChamps == $nbValeurs){
for($i = 0; $i < $nbChamps ; $i++){
if($i < ($nbChamps - 1)){
if(is_numeric($valeurs[$i]))
$Requete = $Requete.$champs[$i]." = ".$valeurs[$i].",";
else
$Requete = $Requete.$champs[$i]." = '".$valeurs[$i]."',";
}
else
if(is_numeric($valeurs[$i]))
$Requete = $Requete.$champs[$i]." = ".$valeurs[$i]." ";
else
$Requete = $Requete.$champs[$i]." = '".$valeurs[$i]."' ";
}
$Requete = $Requete.$this->genereConditions($conditions);
$this->db->query($Requete);
}
else
throw new Exception("Le nombre de champs n'est pas identique au nombre de valeurs", 1);
}
private function genereConditions($conditions){
$condition = "WHERE ";
for($i = 0 ; $i < count($conditions); $i++){
if($i < (count($conditions)) - 1)
$condition = $condition.$conditions[$i]." AND ";
else
$condition = $condition.$conditions[$i];
}
return $condition;
}
I would need a good structure for building queries where search parameters are conditional using mysqli prepared statement. $query -> bind_param('sss',$date,$time,$place);
I dont know how to apply 'sss' and '$date,$time,$place' parameters in order later. Can you pass them as variable?
Old MySQL way:
<?php
// date is obligatory
$date = mysql_real_escape_string($_GET["date"]);
$query="SELECT * FROM dbase WHERE date='$date'";
// time field is custom
if(!empty($_GET["time"])) {
$time= mysql_real_escape_string($_GET["time"]);
$buildQuery[] = "time='$time'";
}
// place field is also custom
if(!empty($_GET["place"])) {
$place= mysql_real_escape_string($_GET["place"]);
$buildQuery[] = "place='$place'";
}
// building query
if(!empty($build)) {
$query .= ' AND '.implode(' AND ',$build).' ORDER BY date';
}
?>
This is a good case where PDO is far easier than MySQLi:
$query="SELECT * FROM dbase";
$terms = array("date" => $date);
$params = array();
// time field is custom
if(isset($_GET["time"])) {
$terms["sType"] = $time;
}
// place field is also custom
if(isset($_GET["place"])) {
$terms["place"] = $place;
}
// building query
if(!empty($terms)) {
$query .= "WHERE " . implode(" AND ",
array_map(function($term) { return "$term = ?"; }, array_keys($terms));
$params = array_values($terms);
}
$query .= "ORDER BY date";
$stmt = $pdo->pepare($query);
$stmt->execute($params);
PS: I have to question whether you meant to have your sType column contain both time and place. Seems like you're breaking relational database best practices. Unless it's just a typo.
untested, but you should be able to do something like this
$types = array();
$vals = array();
if(isset($_GET["time"])) {
$types[] = 's';
$vals[] = $_GET["time"];
$buildQuery[] = "sType = ?";
}
//...etc...
$args = array_merge(array(join($types)), $vals);
$callable = array($mysqli, 'bind_param');
call_user_func_array($callable, $args));
http://php.net/manual/en/language.types.callable.php
but, there's another approach. Just use logic in the sql:
select *
from tbl
where (date = ? or ? = '')
and (time = ? or ? = '')
and (place = ? or ? = '')
The above assumes you'll bind each arg twice, and bind them as strings, but binding an empty string if the query string param wasnt set... You could bind them as null if needed too via something like (date = ? or ? is null).
$mysqli->bind_param('ssssss', $date,$date, $time,$time, $place,$place);
ps, the mysql optimizer will make short work of that simple logic.
Based on goat's reply, here's a full and tested answer with an UPDATE statement where the fields to be updated are conditional. I have used a very simple table structure with 3 fields: ID (auto increment), Varchar1 (varchar255) and Varchar2 (varchar255). In this script, I want to update the two varchar fields for the first three records. Based on this script, it's very easy to conditionally add or remove fields to be updated.
$mysqli = new mysqli(...);
$types = array();
$vals = array();
$query = array();
// varchar1
$types[] = 's';
$vals[] = 'foo1';
$query[] = "varchar1=?";
// varchar2
$types[] = 's';
$vals[] = 'foo2';
$query[] = "varchar2=?";
$sql = "UPDATE test SET ".implode(",", $query)." WHERE id IN (1,2,3)";
$stmt = $mysqli->prepare($sql);
$args = array_merge(array(implode($types)), $vals);
$callable = array($stmt, 'bind_param');
call_user_func_array($callable, refValues($args));
$stmt->execute();
function refValues($arr) {
if (strnatcmp(phpversion(),'5.3') >= 0) //Reference is required for PHP 5.3+
{
$refs = array();
foreach($arr as $key => $value)
$refs[$key] = &$arr[$key];
return $refs;
}
return $arr;
}
For some reason the both code upon killing the query var returns:
SELECT client_fname FROM client WHERE c=:l AND f=:n
Instead of:
SELECT client_fname FROM client WHERE client_id=:id AND client_id=:fname
Notice that only the first character of the column name strings is output.
Where am I going wrong? :S
PHP 5.4, will be using PDO SQL.
public function getField($_field, $_id, $_type) {
$_args = array(
array($_type.'_id', 'id'),
array($_type.'_fname', 'fname')
);
//var_dump($_args);
echo $this->dbSelect($_type.'_'.$_field, $_type, $_args);
}
protected function dbSelect($_select, $_from, $_args) {
$i = 0; //var_dump($_args);
$query = 'SELECT '.$_select.' FROM '.$_from.' WHERE ';
foreach ($_args as $_where) {
if($i == 0) {
$query .= $_where[$i][0] .'=:'. $_where[$i][1];
} else {
$query .= ' AND '.$_where[$i][0] .'=:'. $_where[$i][1];
}
$i++;
}
die($query);
}
$_args was a 2D array. However, your foreach is using $_where as its iteration variable. $_where is itself a one-dimensional array, so you don't need $i here at all. Instead just use $_where[0]
$_where[0] should refer to the column, and $_where[1] refers to its bound placeholder. $i is unrelated to the contents of $_where.
foreach ($_args as $_where) {
if($i == 0) {
$query .= $_where[0] .'=:'. $_where[1];
} else {
$query .= ' AND '.$_where[0] .'=:'. $_where[1];
}
$i++;
}
Otherwise, you are getting the result of an array key access of a string. For example:
$str = "Hello world";
echo $str[0];
// "H"
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);