PHP while() function is running my query 3 times - php

My problem is that:
I have a users table in MySQL. I made a query with MySQLi which looks like:
if($stmt = $mysqli->prepare("SELECT condition,name,money FROM users WHERE fbid = ?")){
$stmt->bind_param('s',$_SESSION['FBID']);
$stmt->execute();
$stmt->store_result();
$num_of_rows = $stmt->num_rows;
$stmt->bind_result($condition,$name,$money);
while ($stmt->fetch()) {
And here's my problem, because I want different users to add equal usernames to their ids. So, my code is the following:
if($_GET['name']!='' && $money>'500'){
$stmt2 = $mysqli->prepare("UPDATE users SET `condition` = `condition` + 5, `money` = `money` - 5 WHERE fbid = ? AND name = ?");
$stmt2->bind_param("ss", $_SESSION['FBID'],$_GET['name']);
$stmt2->execute();
$stmt2->close();
I want to update only that value in the database where user ID = $_SESSION[fbid] and name = $_GET[name]. So if I have an account with id 1922838445 and I have three names, for example, John, Joe, and Jill and $_GET[name]=='Joe' then update only that value at the same ids. It works until that point that update only the got value, but it does that 3 times... Because of while () maybe??? How can I fix it?
The two code samples have to come one after!! Because of checking the value of money..

There's no need for the first SELECT and the loop, just put the condition on money into the UPDATE query itself.
if ($_GET['name'] != '') {
$stmt = $mysqli->prepare("
UPDATE users
SET condition = condition + 5, money = money - 5
WHERE fbid = ? AND name = ?
AND money > 500")
$stmt->bind_param("ss", $_SESSION['FBID'], $_GET['name']);
$stmt->execute();
}

Try this instead to get only one record:
It gets only the record you need because it has a WHERE clause on both parameters that you plan to update, instead of only a partial match on the fbid like you had before.
Before you were getting 3 records because you had a partial key search, then looping through the records and updating the same record over and over against, regardless of the value of the second part of the key in the record you were looping against.
if($stmt = $mysqli->prepare("SELECT condition,name,money FROM users WHERE fbid = ? AND name = ?")){
$stmt->bind_param("ss", $_SESSION['FBID'],$_GET['name']);
$stmt->execute();

Related

How to generate a sequential number that restarts yearly in a web application

I need to generate a code which consists of some arbitrary prefix, a year, and an incrementing number. The incrementing number must start at 1 at the first time when the number is generated that year.
This code needs to be added to the sqlite database and be available elsewhere in the PHP script.
What i have done now uses 4 accesses to the database:
$codePrefix = 'TEST';
$stmt = $db->prepare(
'INSERT INTO test (year)
VALUES(strftime("%Y", "now"))'
);
$stmt->execute();
$id = $db->lastInsertId();
$stmt = $db->prepare('SELECT `year` FROM `test` WHERE `id`=:id');
$stmt->bindValue(':id', $id);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
$year = $result['year'];
$stmt = $db->prepare('SELECT Ifnull(Max(id), 0) `max_id` FROM `test`
WHERE `year`<:year');
$stmt->bindValue(':year', $year);
$result = $stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
$previousMax = $result['max_id'];
$codeSuffix = $id-$previousMax;
$code = "{$codePrefix}-{$year}-{$codeSuffix}";
$stmt = $db->prepare('UPDATE `test` SET `code`=:code WHERE `id`=:id');
$stmt->bindParam(':code', $code);
$stmt->bindParam(':id', $id);
$stmt->execute();
Here i am abusing the fact that the id is an integer primary key, and autoincrements.
This works. But i feel that it is doing something very easy in a very complicated manner.
Is there a better solution? I need to assume that the midnight of the first of January can happen at any moment of the code, so i cannot do things like get the year information from PHP without hitting the database.
Before somebody asks, the reason i am using prepared statements even when no values are bound is because late on obviously more data will be inserted into the table.
Consider a pure SQL solution using the ROW_NUMBER window function. Below assigns to new field, new_id:
UPDATE test
SET new_id = 'TEST_' || test.[Year] || '_' || sub.rn
FROM (
SELECT id,
[Year],
ROW_NUMBER() OVER (PARTITION BY [Year] ORDER BY id) AS rn
FROM test
) AS sub
WHERE test.id = sub.id;

SQL UPDATE Statement does not affect any rows (PHP)

I'am currently working on a project and want to make a simple page where I can edit groups. I had everything working fine in XAMPP and tried uploading it to the server, but it won't affect any rows in the database.This is the statement:
UPDATE user_groups
SET name = 'TEST',
name_short = 'test',
color = 'green',
category = 'MMORPG'
WHERE id = 2
and:
Affected rows (UPDATE): 0
Is the answer. Creating new groups works fine (Local creating and editing works and I did not change anything in the statements since I uploaded both)
This is what the row looks like that I am trying to affect
EDIT:
$sql_update_info = "UPDATE user_groups SET name = '$new_title', name_short = '$new_short', color = '$new_color', category = '$new_cat' WHERE id = $group_id";
$query_update_info = mysqli_query($mysqli, $sql_update_info);
printf("Affected rows (UPDATE): %d\n", mysqli_affected_rows($mysqli));
echo '<br><span style="color:white;">'.$sql_update_info.'</span>';
Is what the PHP part looks like when clicked on the button.
1st : Try to use prepared statement to avoid sql injection.
2nd : Execute() will return true or false so based on that you need to handle the error like below.
$stmt = $mysqli->prepare("UPDATE user_groups SET name = ?, name_short = ?, color = ?, category = ? WHERE id = ?");
$stmt->bind_param('ssssi', $new_title, $new_short, $new_color, $new_cat, $group_id);
//The argument may be one of four types:
//i - integer
//d - double
//s - string
//b - BLOB
//change it by respectively
$r = $stmt->execute();
if(!$r){
echo $stmt->error;
}else{
$row_count= $stmt->affected_rows;
}
$stmt->close();
$mysqli->close();

Delete all rows with a specific ID from 3 different tables using PHP and MySQL

I would like to remove all rows from three different tables that matches a specific ID.
All three tables uses the same ID: chat_id.
How do I delete all rows from three different tables WHERE chat_id = 1234?
Example:
$stmt = $mysqli->prepare('DELETE FROM table1, table2, table3 WHERE chat_id = ?');
I should mention that this is inside a loop see below.
My full code:
$stmt = $mysqli->prepare('SELECT chat_id FROM chat_id WHERE chat_expire < NOW()');
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
$chatid[] = $row['chat_id'];
}
$stmt->close();
foreach($chatid as $id) { // Delete all old posts.
$stmt = $mysqli->prepare('DELETE FROM chat_id, chat_msg, chat_nick WHERE chat_id = ?');
$stmt->bind_param('s', $id);
$stmt->execute();
$stmt->close();
}
Write the same query three times by changing the table name, always in the same function.

Removing voting access for specific object depending on IP Address saved in database

I have tried making a few posts about this problem, but have decided to collect everything in this final one to hopefully somehow solve it.
I am building a site where users can vote on questions from a database. There's no login and so, to make sure everyone can only vote once per question, I am using their IP together with the ID of the question.
First, I get the ID and IP address and store both, making sure they are integers:
if(isset($_GET['id']))
{
//Get IP address
//Test if it is a shared client
if (!empty($_SERVER['HTTP_CLIENT_IP'])){
$ip=$_SERVER['HTTP_CLIENT_IP'];
//Is it a proxy address
}elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])){
$ip=$_SERVER['HTTP_X_FORWARDED_FOR'];
}else{
$ip=$_SERVER['REMOTE_ADDR'];
}
//Save id and IP address as variables
$id = $_GET['id'];
$ip_long = ip2long($ip);
I then check to see if the user has already votes, using the two variables. This is where I expect the problem arises. I get a:
Notice: Trying to get property of non-object
from line 116 which is: $row_cnt = $result->num_rows.
Furthermore var_dump ($result) returns bool(false) and var_dump ($row_cnt) returns Null. Adding quotes around the two variables in the query, $ip_long and $id fixes the problem while localhost, but not on my server.
A local var_dump($result) with quotes around the variables returns the following:
object(mysqli_result)#2 (5) { ["current_field"]=> int(0) ["field_count"]=> int(1) ["lengths"]=> NULL ["num_rows"]=> int(1) ["type"]=> int(0) }
I would like to add 1 to the QuestionVotes for the specific question and then remove the option to vote on that same question for the specific IP Address.
//Save id and IP address as variables
$id = $_GET['id'];
$ip_long = ip2long($ip);
///Check to see if user already voted
$stmt = $conn->prepare("SELECT * FROM User_Votes where UserID = ? and QuestionID = ?");
mysqli_stmt_bind_param($stmt, 'ss', $ip_long, $id);
$stmt->execute();
$result = $stmt->get_result();
if($result->num_rows){
//The user has already voted
echo "Already voted";
}else{
//Add IP Address and ID to the User_Votes table
$stmt = $conn->prepare("INSERT INTO User_Votes (UserID, QuestionID) VALUES (?, ?)");
mysqli_stmt_bind_param($stmt, 'ss', $ip_long, $id);
$stmt->execute();
$stmt = $conn->prepare("UPDATE Question SET QuestionVotes = QuestionVotes + 1 where QuestionID = ?");
mysqli_stmt_bind_param($stmt, 's', $id);
$stmt->execute();
}
}
And lastly, here is the code I use to build the html boxes containing database question information, add a voting button that displays the current votes and append, what is used as QuestionID, to the url:
// Build 4 question boxes from database Question table, including voting button
$stmt = $conn->prepare("SELECT * FROM question ORDER BY QuestionVotes DESC LIMIT 4");
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
//$row["QuestionID"] to add id to url
echo "<div class=\"col-md-3\"><h2>". $row["QuestionHeader"]. "</h2><p>". $row["QuestionText"]. "</p><p> " . $row["QuestionVotes"] . "</p></div>";
}
}
else
{
echo "0 results";
}
My tables are as follows:
Question: QuestionID(int11)(pk), QuestionHeader(varchar(20)), QuestionText(text), QuestionVotes(int(5))
User_Votes: UserID(unsigned, int(39)), QuestionID(int(11))
There are couple of things I would like to point out. First, your error:
I get a 'Notice: Trying to get property of non-object' from line 116 which is: $row_cnt = $result->num_rows;.
When you call mysqli->query() with a select query that finds no results then returned object is not an object but instead false.
Second, instead of COUNT(*), just use *.
So to maintain your logic, you should do something like this:
//Check to see if user already voted
$result = $conn->query("SELECT * FROM User_Votes where UserID = '$ip_long' and QuestionID = '$id'");
if ($result === false) {
//Add IP Address and ID to the User_Votes table
$result = $conn->query("INSERT INTO `User_Votes` (`UserID`, `QuestionID`) VALUES ('$ip_long', '$id')");
}elseif($result && $result->num_rows) {
//The user has already voted
echo "Already voted";
}
Edited:
//Check to see if user already voted
$result = $conn->query("SELECT * FROM User_Votes where UserID = '$ip_long' and QuestionID = '$id'");
if($result->num_rows){
//The user has already voted
echo "Already voted";
}else{
//Add IP Address and ID to the User_Votes table
$result = $conn->query("INSERT INTO User_Votes (UserID, QuestionID) VALUES ('$ip_long', '$id')");
}
Re-edited:
You have to call $stmt->store_result() after $stmt->execute(). And your $stmt->get_result() is unnecessary here because you're not using the selected data.
Part of a comment from the documentation:
If you do not use mysqli_stmt_store_result( ), and immediatley call this function after executing a prepared statement, this function will usually return 0 as it has no way to know how many rows are in the result set as the result set is not saved in memory yet.
So your code should be like this:
if(isset($_GET['id']) && !empty($_GET['id'])){
$id = $_GET['id'];
$ip_long = ip2long($ip);
//Check to see if user already voted
$stmt = $conn->prepare("SELECT * FROM User_Votes where UserID = ? and QuestionID = ?");
$stmt->bind_param('ss', $ip_long, $id);
$stmt->execute();
$stmt->store_result();
if($stmt->num_rows){
//The user has already voted
echo "Already voted";
}else{
//Add IP Address and ID to the User_Votes table
$stmt = $conn->prepare("INSERT INTO User_Votes (UserID, QuestionID) VALUES (?, ?)");
$stmt->bind_param('ss', $ip_long, $id);
$stmt->execute();
$stmt = $conn->prepare("UPDATE Question SET QuestionVotes = QuestionVotes + 1 where QuestionID = ?");
$stmt->bind_param('s', $id);
$stmt->execute();
}
}
Sidenote: Please don't mix the procedural and object oriented style of mysqli.
You should check the name of your table.
You use this in one of the queries User_Votes and this user_votes in another one. It might work on your development server, if it's powered by Windows, that are case insensitive, but Linux, which probably powers your production server case-sensitive is.
Check this question for more informations: Are table names in MySQL case sensitive?
Also note, that from the code above, your app looks insecure to SQL injection. You should cast the variables to int, or what do you expect them to be.
Your insert statement is using single quotes to enclose your variables. Those should be double quotes so PHP will interpret your variables to be the values instead of the literal string.
This looks to be the root cause of what's going on. Were you able to verify everything was being written to your database tables correctly before pulling them to work on? Then verify your select statement was pulling the data correctly and what the form the data took?
And jcaran's comment is correct ... some verification of the variables you grab will need to be considered.

Short code to get 2 values from one row with php pdo

Possibly stupid question, but can not find answer.
I need to get values from two columns of the same row.
And then set variables with each value.
Here I get one value from column Number and then define variable $NumberPostRegister1
$stmt = $db->prepare("SELECT Number FROM 2_1_journal WHERE Number = :Number1");
$stmt->bindParam(':Number1', $row_id1);
$stmt->execute();
$NumberPostRegister1 = $stmt->fetchColumn();
echo $NumberPostRegister1 .' NumberPostRegister1<br>';
Here I get second value from column IfDraft and then define variable $IfDraft1
$stmt = $db->prepare("SELECT IfDraft FROM 2_1_journal WHERE Number = :Number1");
$stmt->bindParam(':Number1', $row_id1);
$stmt->execute();
$IfDraft1 = $stmt->fetchColumn();
echo $IfDraft1 .' NumberPostRegister1<br>';
Two queries and rather long code.
How to do the same using one query and shorter/simpler code?
$stmt = $db->prepare("SELECT IfDraft, Number FROM 2_1_journal WHERE Number = ?");
$stmt->execute(array($row_id1));
list($IfDraft, $Numer) = $stmt->fetch();

Categories