Why I cant update rows with PDO using prepare function? - php

I have an SQL $query like this;
UPDATE `tags` SET `tags.name` = :name,
`tags.slug` = :slug,
`tags.date_published` = :date_published,
`tags.meta_title` = :meta_title,
`tags.meta_description` = :meta_description,
`tags.meta_keywords` = :meta_keywords
WHERE `tags.id` = :id
And my $values array;
Array
(
[:name] => iphone
[:slug] => iphone
[:date_published] => 2016-03-27
[:meta_title] => iphone Yazıları
[:meta_description] => iphone hakkında son gelişmeler ve faydalı bilgiler.
[:meta_keywords] => iphone yazıları, iphone hakkında bilgiler, iphone haberleri
[:id] => 24
)
I'm trying to update one row with PDO's prepare function;
$sth = $this->db->prepare($query);
$sth->execute($values);
echo "affected rows: ".$sth->rowCount(); // prints 0
return $sth->rowCount() ? true : false;
And I get no errors but rows are not affected after execute the query. Where is my mistake?
mysql tags table
edit:
I'm creating values array like this;
$params = array(
"id", "name", "slug", "date_published",
"meta_title", "meta_description", "meta_keywords");
$values = array();
foreach ($params as $key) {
#$values[$key] = $_POST[$key];
}
And this is how I create the query;
$query = "UPDATE `$table` SET";
$values = array();
/* Add field names and placeholders to query. */
foreach ($params as $key => $value) {
$query .= " `{$table}.{$key}` = :" . $key . ",";
$values[":" . $key] = $value;
}
/* Remove last comma. */
$query = substr($query, 0, -1);
/* Build where condition. */
if ($cond) {
$query .= " WHERE ";
foreach ($cond as $key => $value) {
$query .= " `{$table}.{$key}` = :" . $key . ",";
$values[":" . $key] = $value;
}
/* Remove last comma. */
$query = substr($query, 0, -1);
}

You're escaping the column names incorrectly. For example, the back ticks should not enclose the whole of tags.name, but rather the table and column names separately:
`tags`.`name`
Checking the output of PDO::errorInfo() confirms this (emulated prepares must be set to false in order to see this, however).
General notes:
You don't need to specify the colon prefix for the keys of the array being bound (likewise when explicitly binding columns via bindParam() or bindValue()).
You should avoid using back ticks in your PDO queries. They're a MySQL-specific feature, and PDO is not a SQL abstraction layer (thereby preventing one of the major benefits of PDO - portability).

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
}

PDO update same value [duplicate]

The code I've written so far works fine if there is only one named place holder for a prepared statement but if there are multiple conditions for a query, it doesn't return any results from the database.
For instance:
$query = array();
$query['columns'] = array('*');
$query['tables'] = array('esl_comments');
$query['where'] = array(
'esl_comments.commentVisible' => array('=', 'Y')
);
Works fine. But if I try:
$query = array();
$query['columns'] = array('*');
$query['tables'] = array('esl_comments');
$query['where'] = array(
'esl_comments.commentVisible' => array('=', 'Y'),
'esl_comments.commentID' => array('=', '1'),
);
(Note the additional commentID parameter) it fails to return anything despite there being data in the mySQL database that satisfies the conditions.
The PDO code i've written is:
$sql ='SELECT ';
foreach($query['columns'] as $column){ //What columnns do we want to fetch?
$sql.=$column . ", ";
}
$sql = rtrim($sql, " ,");
$sql .=' FROM '; //Which tables will we be accessing?
foreach($query['tables'] as $tables){
$sql.=$tables . ", ";
}
$sql = rtrim($sql, " ,"); //Get rid of the last comma
$sql .=' WHERE ';
if(array_key_exists('where', $query)) //check if a where clause was provided
{
$fieldnames = array_keys($query['where']);
$count = 0;
$size = sizeof($fieldnames);
$bindings = array();
foreach($query['where'] as $where){
$cleanPlaceholder = str_replace("_", "", $fieldnames[$count]);
$cleanPlaceholder = str_replace(".", "", $cleanPlaceholder);
$sql.=$fieldnames[$count].$where[0].":".$cleanPlaceholder." AND ";
$bindings[$cleanPlaceholder]=$where[1];
$count++;
}
$sql = substr($sql, 0, -5); //Remove the last AND
}
else{ //no where clause so set it to an always true check
$sql.='1=1';
$bindings=array('1'=>'1'); //Provide default bindings for the statement
}
$sql .= ';'; //Add the semi-colon to note the end of the query
echo $sql . "<br/><br/>";
// exit();
$stmt = $this->_connection->prepare($sql);
foreach($bindings as $placeholder=>$bound){
echo $placeholder . " - " . $bound."<br/>";
$stmt->bindParam($placeholder, $bound);
}
$result = $stmt->execute();
echo $stmt->rowCount() . " records<br/>";
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
I'm building queries dynamically and therefore I am cleaning the placeholders, by stripping them of periods and underscores - hence the use of the 'cleanPlaceholder' variable.
The query being generated looks like this:
SELECT * FROM esl_comments WHERE esl_comments.commentVisible=:eslcommentscommentVisible AND esl_comments.commentID=:eslcommentscommentID;
And the parameters being bound look like this:
eslcommentscommentVisible - Y
eslcommentscommentID - 1
bindParam Requires a reference
The problem is caused by the way you bind parameters in the foreach loop.
foreach($bindings as $placeholder=>$bound){
echo $placeholder . " - " . $bound."<br/>";
$stmt->bindParam($placeholder, $bound);
}
bindParam requires a reference. It binds the variable, not the value, to the statement. Since the variable in a foreach loop is reset at the start of each iteration, only the last reference to $bound is left intact, and you end up binding all your placeholders to it.
That's why your code works when $query['where'] contains only one entry, but fails when it contains more than one.
You can solve the problem in 2 ways:
Pass by reference
foreach($bindings as $placeholder => &$bound) { //pass $bound as a reference (&)
$stmt->bindParam($placeholder, $bound); // bind the variable to the statement
}
Pass by value
Use bindValue instead of bindParam:
foreach($bindings as $placeholder => $bound) {
$stmt->bindValue($placeholder, $bound); // bind the value to the statement
}

Convert a PHP array to an SQL statment?

I'm trying to convert an array (key/value) to be an SQL statement.
I'm using MYSQLi like such:
if(!$result = $mysqli->query($sql)){throw new Exception("SQL Failed ".__file__." on line ".__line__.":\n".$sql);}
I have an array like such:
Array
(
[database] => Array
(
[cms_network] => Array
(
[network_id] => 61
[network_name] =>
[network_server_mac_address] => 00:1b:eb:21:38:f4
[network_description] => network
[network_thermostat_reporting_rate] => 5
[network_server_reporting_rate] => 5
[network_data_poll_rate] => 5
[network_created_by] => 38
[network_modified_by] => 1
[network_network_id] => 8012
[network_language] => en
[network_hotel_id] => 68
[network_channel] => 0
[network_deleted] => 0
[network_reported_network_id] => 8012
[network_rooms] => 4
)
)
)
How can I convert [cms_network] to look like this:
$sql = "UPDATE cms_network set network_id='61', network_name='',
network_server_mac_address = '00:1b:eb:21:38:f4', .... WHERE network_id='61'"
I'm more interested in knowing how to concatenate the key=>value pair of the array to be key='value' in my select statement.
Thanks for the help!
If you use the VALUES syntax, you could do it in one fell swoop.
mysql_query("
UPDATE MyTable
( . implode(',', array_keys($array['database']['cms_network'])) . ")
VALUES ('" . implode("','", $array['database']['cms_network']) . "')
");
This, of course, assumes that the data is already escaped.
EDIT: Tidier version that's easier to read and maintain:
$fields = implode(',', array_keys($array['database']['cms_network']));
$values = implode("','", $array['database']['cms_network']);
mysql_query("UPDATE MyTable ($fields) VALUES ('$values')");
I suggest you populate an array with formatted key/value pairs, then implode them at the end. This is an easy way to add the required , between each key/value:
$fields = array();
foreach($array['database']['cms_network'] as $key => $value) {
// add formatted key/value pair to fields array
// e.g. format: network_id = '26'
$fields[] = $key . " = '" . $value . "'";
}
$fields = implode(', ', $fields);
// build your query
$query = "UPDATE cms_network SET " . $fields . " WHERE network_id = " . $array['database']['cms_network']['network_id'] . " LIMIT 1";
// process it...
This will (SQL wise) be inserting every value as a string, which is obviously incorrect with integer columns etc. It should still work anyway, but if not you'll need to put in a conditional statement for whether to wrap the value in quotes or not, like this:
foreach(...) {
if(is_numeric($value))
$fields[] = $key . ' = ' . $value;
else
$fields[] = $key . " = '$value'";
}
Although this should probably relate to your database column type rather than the PHP variable type. Up to you, they should work fine with quotes around integers.
This should work.
$update_query = "UPDATE `cms_network` SET ";
$count = 0;
foreach($array['database']['cms_network'] as $key => $value) {
if ($count != 0) {
$update_query = $update_query.",".$key."=".$value;
} else {
$update_query = $update_query.$key."=".$value;
}
$count++;
}
$update_query = $update_query." WHERE ".cms_network."=".$array['database']['cms_network'];
mysql_query($update_query);

Is it possible to create a MYSQL query based on all "unknown" $_GET parameters of the url?

I know for example if you KNOW what parameters to expect you can simply use
$_GET['parameter']
in your MYSQL query. What if you expect 4-5 parameters, however maybe NOT all of them are passed? For example, let's say the user wants to list all the products which have a price more than 20$ and a warranty of 2 years.
The user could have more search options like for example, the product category should be 2 which is for Laptops
now, my question is, should I have many if statements and check for each possible parameter to see if they are set, and if they are then include them in my MYSQL query, or is there a faster way?
thanks in advance
you should try foreach. Something like:
foreach($_GET as $key => $value) {
switch($key) {
case "category":
$str[] = "category = '". mysql_real_escape_string($value) ."'";
break;
case "price":
$str[] = "price > '". mysql_real_escape_string($value) ."'";
break;
case "warranty":
$str[] = "warranty > '". mysql_real_escape_string($value) ."'";
break;
}
}
//create the mysql string with
$str_where = "WHERE " . implode('AND', $str);
you can uset he str_where string to filter the products.
For security reasons I would definitely list the available keys with their datatypes that can be used in the query first.
$keys = array(
'category' => PDO::PARAM_INT,
'price' => PDO::PARAM_INT,
'etc' => PDO::PARAM_STR
);
// all the available keys for the query and their types
$queryStr = "SELECT * FROM `some_table` WHERE `yourCond`='someVal'";
// initial query you have
$userVals = array();
foreach ($_GET as $key => $value) {
$k = strtolower($key);
if (!in_array($k,array_keys($keys)) || empty($value))
// if the key is not listed in the keys array
// or the value is empty we skip it
continue;
$queryStr .= " AND `$k` = :{$k} ";
// modify query
$userVals[$k] = $value;
// and add the key-value pair into user values array
}
$db = new PDO('mysql:host=someHost;dbname=someDB','someUsername','somePassword');
// create DB connection
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
// enable error reporting
$stmt = $db->prepare($queryStr);
// prepare query string
foreach ($userVals as $k => $v) {
$stmt->bindParam(":{$k}",$v,$keys[$k]);
// bind each parameter with the right datatype
}
$stmt->execute();
// and execute the query

access multidimensional array

This is sort of a follow up to my last question. Im trying to access the data of a multidimensional array so as to insert into a database. Ive been looking all over the forums as well as trying different things on my own but cant find anything that works. Here is what the array looks like:
$_POST[] = array[stake](
'stakeholder1' => array(
'partner'=> 'partner',
'meet'=> 'meet'
),
'stakeholder2' => array(
'partner'=> 'partner',
'agreement'=> 'agreement',
'train'=> 'train',
'meet'=> 'meet'
),
);
I'm trying to do somthing like ( UPDATE list WHERE stakeholder = "stakeholder1" SET partner =1, meet =1 ) depending on which stakeholder/choices were selected (theres four different options). Thanks,
This one will set 1 for checked options, and 0 for unchecked options (otherwise you will miss some data updates).
$optionsList = array('partner', 'agreement', 'train', 'meet');
$stakeHolders = array('stakeholder1', 'stakeholder2', ...);
foreach($stakeHolders as $stakeHolder)
{
$selectedOptions = $_POST[$stakeHolder];
$arInsert = array();
foreach($optionsList as $option)
$arInsert[] = '`'.$option.'` = '.intval(isset($selectedOptions[$option]));
$sql = "UPDATE list
SET ".implode(", ", $arInsert)."
WHERE stakeholder = '".mysql_real_escape_string($stakeHolder)."'";
// TODO: execute $sql (mysql_query(), or PDO call,
// or any wrapper you use for DB)
}
$query = '';
foreach ($_POST as $k => $v) {
$query .= "UPDATE list WHERE stakeholder = '$k' SET " . implode(" = 1," $v) . ",";
}
$query = substr($query, 0, -2); //Get rid of the final comma
This should give you something like what you are looking for, if I understand your database schema correctly.
If you are using PDO, you could do something like:
foreach($_POST as $stakeholder => $data) {
$sth = $dbh->prepare('UPDATE `list` SET `' . implode('` = 1, `', array_keys($data)) . '` = 1 WHERE stakeholder = ?');
$sth->execute(array($stakeholder));
}
Be sure to sanitize the user input (keys of the data...).
foreach($main_array as $key => $sub_array) {
$sql = 'UPDATE list
WHERE stakeholder = "{$key}"
SET ';
$sets = array();
foreach($sub_array as $column_value)
$sets[] = $column_value." = 1";
$sql = $sql.implode(',', $sets);
}

Categories