Issues incrementing a field in MySQL/PHP with prepared statements - php

I have the following code which is supposed to increment a field value by 1 in a prepared PHP mysql statement:
function db_OP_doVote($pdo, $postid, $votetype)
{
$prepStatement = $pdo->prepare(
"UPDATE content_posts SET `:votetype` = `:votetype` + 1 WHERE `id` = :id"
);
$prepStatement->execute(array(':votetype' => $votetype, ':id' => $postid));
echo "Success";
}
This however, does nothing. No error is thrown back about incorrect SQL syntax and the script runs to completion, but my field does not update at all.
The values for this script are fed through a jQuery post() to this script:
//isset checking here
$postID = (int)$_POST['id'];
$voteType = $_POST['type'];
if ($voteType == "y")
{
$trueType = "v-cool";
}
elseif ($voteType == "m")
{
$trueType = "v-meh";
}
elseif ($voteType == "n")
{
$trueType = "v-shit";
}
else
{
die();
}
$db = db_Connect();
db_OP_doVote($db, $postID, $trueType);
Which also appears to filter the values and send them fine. I can't wrap my head around what the issue could be. The field being incremented is a BIGINT(20).
What am I missing?
EDIT: Solved the issue.
N.B's comment hit the nail on the head - binding the column name causes it to be quoted, which invalidates the query. Thanks!

you can't use binding for the field names.
from the question it seems that your setup is wrong.
you should have another table with votes and vote types as data.

You can't parameterize column names with PDO. What you can do is have hard-coded values (which you basically already have) and construct the SQL string accordingly. I would check this value in the actual function too though, just to be on the safe side:
function db_OP_doVote($pdo, $postid, $votetype)
{
if( !in_array( $votetype, array( 'v-cool', 'v-meh', 'v-shit' /*, etc. */ ), true ) )
{
throw new InvalidArgumentException( 'Unexpected $votetype: ' . $votetype );
// or simply return false perhaps
}
$sql = '
UPDATE content_posts
SET `' . $votetype . '` = `' . $votetype . '` + 1
WHERE `id` = :id
';
$prepStatement = $pdo->prepare( $sql );
$prepStatement->execute(array(':id' => $postid));
echo "Success";
}
However, this strategy suggests your database design could use a little more attention. The way you have it now, is that for every type of vote, you have a column. This is not really efficient and/or flexible database design. What happens if you get asked to add another type of vote?
I'd suggest adding another table, to be more flexible:
CREATE TABLE `content_post_vote` (
`content_post_id` int(11) NOT NULL,
`vote_type` enum('cool','meh','shit') NOT NULL, # using enum() to assure valid vote types
`votes` bigint(20) DEFAULT NULL,
PRIMARY KEY (`content_post_id`,`vote_type`)
)
Then your query would be something like:
$sql = '
INSERT INTO `content_post_vote` (`content_post_id`,`vote_type`,`votes`)
VALUES( :id, :votetype, 1 )
ON DUPLICATE KEY UPDATE `votes` = `votes` + 1
';
What this does is insert a vote if there is no record for a certain primary key (content_post_id,vote_type) yet, and else update the record with a vote if the record already exists.
Then to query the database for how many votes of a particular type a particular content_post has gotten, you do this:
$sql = '
SELECT `votes` # or perhaps more columns
FROM `content_post_vote`
WHERE `content_post_id` = :id AND
`vote_type` = :votetype
';

Related

Creating an SQL Query based on data from an Array

I'm trying to find a way to simplify an existing function which communicated with our database. The function currently has several parameters (upwards of 15), and everytime a record is added or updated, all the parameters are required.
I have the following PHP Function (simplified):
function addSomethingToDB($var1, $var2, $var3, $var4...) {
# Do SQL Injection checks
...
$query = 'INSERT INTO `table` (`var1`,`var2`,`var3`,`var4`) VALUES ($var1, $var2, $var3, $var4)';
# OR
$stmt = $db->prepare('INSERT INTO `table` (`var1`,`var2`,`var3`,`var4`) VALUES (?,?,?,?)');
$stmt->bind_param('ssss', $var1, $var2, $var3, $var4);
}
The above code obviously gets pretty messy if you have more than a few variables. And it's difficult to work with if not all variables are required. Because of this I attempted a second scenario where I either had one main/required parameter followed by an array or I just had an array as the parameter.
function addSomethingToDB($var1, array $attributes) {}
The goal here was to allow the array to have a more flexible approach in case the SQL query either needs to be extended in the future, or to build the SQL query based on optional values.
For example:
If Var2 and Var4 are not provided, the array would look like:
{
'var1': 'Var1_Value',
'var3': 'Var3_Value'
}
and the SQL would be:
$query = 'INSERT INTO `table` (`var1`,`var3`) VALUES ($var1, $var3);
As you can see, in the above scenario, the query was adapted for only the necessary values.
What I want to try and achieve is to build the SQL query based on the values provided. The first was I assume would be to have an IF ELSE statement or a SWITCH. Which gives me something weird like the following:
function getlogs($type, $id='') {
$types = array('client_log', 'system_log', 'user_log', 'api_log', 'project_log', 'lead_log');
if (in_array($type, $types)) {
if ('client_log' == $type) {
if (!empty($id)) {
$query = 'SELECT * FROM `logs` WHERE `client_id` = ' . $id . ' AND `type` = "client_log"';
} else {
$query = 'SELECT * FROM `logs` WHERE `type` = "client_log"';
}
} elseif ('project_log' == $type) {
if (!empty($id)) {
$query = 'SELECT * FROM `logs` WHERE `project_id` = ' . $id . ' AND `type` = "project_log"';
} else {
$query = 'SELECT * FROM `logs` WHERE `type` = "project_log"';
}
} elseif ('user_log' == $type) {
if (!empty($id)) {
$query = 'SELECT * FROM `logs` WHERE `staff_id` = ' . $id . ' AND `type` = "staff_log"';
} else {
$query = 'SELECT * FROM `logs` WHERE `type` = "staff_log"';
}
} elseif ('lead_log' == $type) {
if (!empty($id)) {
$query = 'SELECT * FROM `logs` WHERE `client_id` = ' . $id . ' AND `type` = "lead_log"';
} else {
$query = 'SELECT * FROM `logs` WHERE `type` = "lead_log"';
}
} else {
$query = 'SELECT * FROM `logs` WHERE `type` = ' . $type;
}
$logs = Config::$db->query($query);
return $logs->fetch_all(MYSQLI_ASSOC);
} else {
return 'invalid log type';
}
$stmt->close();
}
The above is not quite the code I want to be writing, it's a similar example where the query related to the Log Type is being called. But that is a lot of mess that is not pleasing to look at. Also, the above code does not use Arrays which is what I hope to be using.
Lastly, the code I am hoping to write is mostly related to Updating existing records. So, say we have a User. The user has an Email and a Password and Address. According to the above code (first one) we will be updating the Email, Password, and Address every time the user updates any one of his field. I would like to avoid that.
My assumption is that I'd have to do something like so:
# 1. Loop Array using maybe For or Foreach
# 2. Have a whitelisted array of allowed values.
# 3. Append to query if an Array value exists.
# 4. Run query.
I fear my problem is at Point 3. I can't seem to figure out how to build the query without going through a lot of messy IF ELSE statements.
Now, by this point, I have certainly searched around SO to find a similar question, however, searches related to SQL and Arrays are almost entirely related to adding "multiple rows in a single SQL query" or something similar.
You can approach this by using arrays, in which keys are column name and containing the values
$columns = [
'field1' => 'value1',
'field2' => 'value2',
'field3' => 'value3',
'field4' => 'value4'
];
addSomethingToDB($columns);
function addSomethingToDB($cols){
# Do SQL Injection checks
$query = "INSER INTO `tablename` ( ".implode(",",array_keys($cols))." ) VALUES ( '".implode("','",array_values($cols))."' )";
}

how to get columns in a table in mysql with php?

I create a table in my database using this code
$con = mysqli_connect($host,$username,$password,$db);
$sql = "CREATE TABLE My_Table(";
for($i = 1; $i<=50 ; $i++) {
if($i!=50)
$sql .= "id_".$i." INT(30) NOT NULL DEFAULT '0',";
else
$sql .= "id_".$i." INT(30) NOT NULL DEFAULT '0')";
}
if (mysqli_query($con,$sql)) {
echo "Done";
}
Now I have $var = 10; and I want to get for example id_10 and change the value of id_10 to id_10+=$var.
I'm a beginner in PHP/MySQL. Thanks in advance.
select:
my $stmt = $con->prepare($con,"SELECT id_".$var." FROM My_table WHERE ...");
$stmt->bind_param($stmt, /* something here which depends on what you need for your condition */ );
$stmt->execute();
update:
my $stmt = $con->prepare($con,"UPDATE My_table SET id_".$var."=id_".$var."+? FROM My_table WHERE ...");
$stmt->bind_param($stmt,"i",$var /* to be changed depending on condition */);
$stmt->execute();
Note that you must make sure that $i is very strictly checked if it comes from the browser ($_GET, $_POST, $_COOKIE...). Otherwise that opens the door to horrible SQL injection errors.
But note my comment above, I don't think this is a good schema.

update profile php

I have create a profile page in php. The page includes the address and telephone fields and prompts the users to insert their data. Data are then saved in my table named profile.
Everything works fine, but the problem is that the table updated only if it includes already data. How can I modify it (probably mysql query that I have in my function), so that data will be entered into the table even if it is empty. Is there a something like UPDATE OR INSERT INTO syntax that I can use?
Thanks
<?php
if ( isset($_GET['success']) === true && empty($_GET['success'])===true ){
echo'profile updated sucessfuly';
}else{
if( empty($_POST) === false && empty($errors) === true ){
$update_data_profile = array(
'address' => $_POST['address'],
'telephone' => $_POST['telephone'],
);
update_user_profile($session_user_id, $update_data_profile);
header('Location: profile_update.php?success');
exit();
}else if ( empty($errors) === false ){
echo output_errors($errors);
}
?>
and then by using the following function
function update_user_profile($user_id, $update_data_profile){
$update = array();
array_walk($update_data_profile, 'array_sanitize');
foreach($update_data_profile as $field => $data )
{
$update[]='`' . $field . '` = \'' . $data . '\'';
}
mysql_query(" UPDATE `profile` SET " . implode(', ', $update) . " WHERE `user_id` = $user_id ") or die(mysql_error());
}
I'm new to the posted answer by psu, and will definatly check into that, but from a quick readthrough, you need to be very careful when using those special syntaxes.
1 reason that comes to mind: you have no knowledge of what might be happening to the table that you're inserting to or updating info from. If multiple uniques are defined, then you might be in serious trouble, and this is a common thing when scaling applications.
2 the replace into syntax is a functionality i rarely wish to happen in my applications. Since i do not want to loose data from colomns in a row that was allready in the table.
i'm not saying his answer is wrong, just stating precaution is needed when using it because of above stated reasons and possible more.
as stated in the first article, i might be a newbie for doing this but at this very moment i prefer:
$result = mysql_query("select user_id from profile where user_id = $user_id limit 1");
if(mysql_num_rows($result) === 1){
//do update like you did
}
else{
/**
* this next line is added after my comment,
* you can now also leave the if(count()) part out, since the array will now alwayss
* hold data and the query won't get invalid because of an empty array
**/
$update_data_profile['user_id'] = $user_id;
if(count($update_data_profile)){
$columns = array();
$values = array();
foreach($update_data_profile as $field => $data){
$columns[] = $field;
$values[] = $data;
}
$sql = "insert into profile (" . implode(",", $columns) .") values ('" . implode("','", $values) . "')" ;
var_dump($sql); //remove this line, this was only to show what the code was doing
/**update**/
mysql_query($sql) or echo mysql_error();
}
}
You cannot update the table if there isn't any data in it corresponding the user_id, meaning that you must have a row containing the user_id and null or something else for the other fields.
a) You can try to check if the table contains data and if not insert it else use update (not ideal)
$result = mysql_query("UPDATE ...");
if (mysql_affected_rows() == 0)
$result = mysql_query("INSERT ...");
b) Checkout this links
http://www.kavoir.com/2009/05/mysql-insert-if-doesnt-exist-otherwise-update-the-existing-row.html
http://dev.mysql.com/doc/refman/5.0/en/replace.html
http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
#Stefanos
you can use use "REPLACE INTO " command in place of "INSERT INTO" in the SQL query.
for example
Suppose you have insert query
INSERT INTO EMPLOYEE (NAME,ADD) values ('ABC','XYZZ');
Now you can use following query as combination of insert and update
REPLACE INTO EMPLOYEE (NAME,ADD) values ('ABC','XYZZ');
Hope this will help!

Using Insert and Update in a single function

I was wondering if someone would be able to shed some light on how I may overcome this problem.
I'm trying to add and update information on a database, so when a user first enters completes the questionnaire its fine and it works, However when they go back to update the questionnaire it throws an error, "Please go back and try again".
I have updated the PHP code with the recommendations given to me so far.
Thank You.
PHP code:
function updatePartCTQ_part1($questionAns, $memberid) {
//First Insert MemberID
$ctqmemberinsert = "INSERT INTO ctq_questionnaire (user_id) VALUES ('$memberid')";
$addresult = mysqli_query($ctqmemberinsert);
if ($addresult) {
$update = "UPDATE ctq_questionnaire SET Item1= '{$questionAns[0]}', Item2 = '{$questionAns[1]}' WHERE user_id = '$memberid'";
mysqli_query($conn, $update);
} else {
echo 'Please go back and try again';
}
}
Any help will be greatly appreciated.
Finished Code
Thanks to Michael and the rest of the guys I was able to get the code working, so I thought I'd post an update, if anyone else gets stuck they'd be able to have a glance at the working version of the code:
function updatePartCTQ_part1($questionAns, $memberid) {
//Check whether user exists
$exists = mysql_query("SELECT * FROM ct1_questionnaire WHERE user_id = '$memberid'");
if (mysql_num_rows($exists) === 0) {
// Doesn't exist. INSERT User into Table
$ctqmemberinsert = "INSERT INTO ctq_questionnaire (user_id) VALUES ('$memberid')";
mysqli_query($ctqmemberinsert);
}
// UDPATE after INSERT
$update = "UPDATE ctq_questionnaire SET Item1= '{$questionAns[0]}', Item2 = '{$questionAns[1]}, Item3 = '{$questionAns[2]}',
Item4 = '{$questionAns[3]}',Item5 = '{$questionAns[4]}', Item6 = '{$questionAns[5]}', Item7 = '{$questionAns[6]}',
Item8 = '{$questionAns[7]}', Item9 = '{$questionAns[8]}', Item10 = '{$questionAns[9]}', Item11 = '{$questionAns[10]}',
Item12 = '{$questionAns[11]}', Item13 = '{$questionAns[12]}', Item14 = '{$questionAns[13]}', Item15 = '{$questionAns[14]}'
WHERE user_id = '$memberid'";
mysql_query($update);
}
Your UPDATE syntax is incorrect. You must not repeat the SET keyword:
$update = "UPDATE ctq_questionnaire SET Item1= '{$questionAns[0]}', Item2 = '{$questionAns[1]}' WHERE user_id = '$memberid'";
//-------------------------------------------------------------^^^^^^^ no SET here
For readability it is recommended to enclose the array values in {}, although your way should work.
Note that your try/catch isn't going to be of much use since mysql_query() does not throw an exception. Instead it will just return FALSE on error. Instead, store it in a variable and test for TRUE/FALSE as you did with the INSERT.
// We assume these values have already been validated and escaped with mysql_real_escape_string()...
$update = "UPDATE ctq_questionnaire SET Item1= '{$questionAns[0]}', Item2 = '{$questionAns[1]}' WHERE user_id = '$memberid'";
$upd_result = mysql_query($update);
if ($upd_result) {
// ok
}
else {
// error.
}
Finally, and I suspect you've heard this before, the old mysql_*() functions are scheduled for deprecation. Consider moving to an API which supports prepared statements, like MySQLi or PDO.
Update
Assuming you have a unique index or PK on ctq_questionnaire.user_id on subsequent calls, the first query will error and your second won't be run. The simplest fix is to use INSERT IGNORE, which will treat key violations as successful.
$ctqmemberinsert = "INSERT IGNORE INTO ctq_questionnaire (user_id) VALUES ('$memberid')";
A more complicated solution is to first test if the username exists in the table with a SELECT, and if not, do the INSERT.
$exists_q = mysql_query("SELECT 1 FROM ct1_questionnaire WHERE user_id = '$memberid'");
if (mysql_num_rows($exists_q) === 0) {
// Doesn't exist. Do the INSERT query
}
// proceed to the UDPATE after INSERTing if necessary
Just change your insertion to this:
$ctqmemberinsert = "INSERT INTO `ctq_questionnaire` (`user_id`, `Item1`, `Item2`)
VALUES ( '$memberid', '" .
mysql_real_escape_string($questionAns[0]) . "', '" .
mysql_real_escape_string($questionAns[1]) . "' )";

Issues with when writing to database

I'm unable to write to my database while using this script that I whipped up earlier.
<?php
include("db.php");
if($_SERVER["REQUEST_METHOD"] == "POST")
{
// Data sent from form, then posted to "admin" table in database
$name = mysql_real_escape_string($_POST['name']);
$description = mysql_real_escape_string($_POST['description']);
$author = mysql_real_escape_string($_POST['author']);
$image = mysql_real_escape_string($_POST['image']);
$category = mysql_real_escape_string($_POST['category']);
$sql = "INSERT INTO admin(name,description,author,image,category) VALUES('$name','$description','$author','$image','$category');";
$result = mysql_query($sql);
header("Location: video.php?file=' . $filename . '");
}
?>
And here's my SQL:
CREATE TABLE admin
(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) UNIQUE,
description VARCHAR(50) UNIQUE,
author VARCHAR(50) UNIQUE,
image VARCHAR(50) UNIQUE,
category VARCHAR(50) UNIQUE
);
Everything is submitted with POST via an HTML form. I'm not really sure what I'm doing wrong, so that why I'm wondering what you guys think. Any thoughts?
$result = mysql_query($sql) is not valid (no connection specified).
It needs to be $result = mysql_query($sql, [CONNECTION]);
There may be other issues, but that's an obvious one.
Follow these steps:
Open a MySQL connection (if not omitted in the snippet)
Check your MySQL statement by using var_dump($sql)
Check for the return value of mysql_query(), should be true if the INSERT statement succeeded.
Check for the number of rows affected by the INSERT statement: mysql_affected_rows()
Note:
I'm pretty sure that your INSERT statement fails because all your columns are defined as UNIQUE. As soon as you already have an author with the same name the statement fails!
$auhtor=mysql_real_escape_string($_POST['author']);
The Author variable is spelled wrong.

Categories