Basic anonymous user rating system - not working - php

I'm working on a rating system and the html/jquery codes works well so far:
Short Example
The idea is that users can rate specific items with 1 (bad), 2 (neutral) or 3 (good). In my code, all thumbs that belong to one item share the same id, e.g.
<i class="far fa-thumbs-up fa-1.5x" data-index="0" id="0"></i>
<i class="far fa-thumbs-up fa-1.5x" data-index="1" id="0"></i>
<i class="far fa-thumbs-up fa-1.5x" data-index="2" id="0"></i>
Then, I store all ratings in 'ratedArray' using the index of the item as the place for the rating at the in the array, so for the short example the corresponding 'ratedArray' the array is
ratedArray = [1, 2, 0, 2]
(you always have to add 1 for the actual rating, so 1 in the array means 2 thumbs or neutral)
Now I want to save this array and the userID (which I just store a new one in the local storage for each visitor) to my DB with
function saveToTheDB(){
$.ajax({
url: "betrayal.php",
method: "POST",
dataType: 'json',
data: {
save: 1,
uID: uID,
ratedArray: ratedArray
}, success: function(r) {
uID = r.id;
localStorage.setItem('uID', uID);
}
});
}
So far so good but now the trouble starts due to my lack of skill (just started with web design). To my first question: I will have 17*4 = 68 items that can be rated for. Since I plan to store the ratings with a unique ID, I wanted to make the DB look like this
id | 0 1 2 3 ... 68 |
1 | 1 0 2 2 ... 0 |
2 | ... |
because I want to be able to calculate the average rating for each item to color the background depending on the average value. Does this make sense or is there a better way to store my data?
My second question is how I am able to store the array properly in the db. There should be 2 options available: create new entry if user id is not in db or update entries if user id is already in db. My current (not working) code looks like
// connect to the data base
$conn = new mysqli( 'localhost', 'root', '', 'db');
if (isset($_POST['save'])){
// ???
$uID = $conn->real_escape_string($_POST['uID']);
$rateArray = $conn->real_escape_string($_POST['ratedArray']);
// create star rating for new uID
if (!$uID) {
$conn->query( "INSERT INTO db (id, 0, 1, 2, 3, *etc*) VALUES ('$uID', '$rateArray')");
$sql = $conn->query( "SELECT ID FROM db ORDER BY ID DESC LIMIT 1");
$uData = $sql->fetch_assoc();
$uID = $uData['ID'];
// update star rating if uID already exists
} else
//???;
exit(json_encode(array('id' => $uID)));
}
Also, could I use the IP adress instead of the userID to make the rating system more reliable?
I'd be greatful, if you could give me some basic tips. Cheers,
M.

As I wrote you above, I came with a simple database schema so you can better understand how you should build your application. It will seem that is more complex than what you did, but its not. Please check it and ask if anything is unclear in comments.
Only after you understand why you should implement this basic schema from bellow, we should move to PHP and jQUERY code.
Tip: use phpMyAdmin tool as a graphical interface to manage your database.
Database logical scheme
PHP Basic Logic
<?php
// our answer will be a valid JSON
header('Content-Type: application/json');
// function that encodes our response
function respond($status, $arr = array()){
// add our status, to final answer
$arr['status'] = $status;
if ($status != 'success'){
if ($status != 'internal_server_error'){
// Specific to server side errors
header("HTTP/1.1 500 Internal Server Error");
} else {
// Specific to client side errors
header("HTTP/1.1 400 Bad Request");
}
}
echo json_encode($arr, JSON_PRETTY_PRINT);
exit();
}
if (isset($_POST['item_id'], $_POST['vote_given'])){
// validate user data
$errors = array();
if (!is_numeric(($_POST['item_id'])){
$errors['item_id'] = 'value is not numeric';
}
if (!is_numeric(($_POST['vote_given'])){
$errors['vote_given'] = 'value is not numeric';
} else {
$vote_given = (int)$_POST['vote_given'];
if ($vote_given < 0 || $vote_given > 2){
$errors['vote_given'] = 'value is outside accepted interval [0,2]';
}
}
// if there are errors, then throw them back to user
if (count($errors) > 0){
respond(
'field_validation_errors',
$errors
);
}
$item_id = $_POST['item_id'];
$user_finger_print = sha1($_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT'])
// At this point we have three numeric values to work with
// item_id, vote_given, user_fingerpint
// SQL logic
$link = mysqli_connect("localhost", "my_user", "my_password", "world");
/* check connection */
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");
/* check connection */
if (mysqli_connect_errno()) {
respond(
'internal_server_error',
array(
'code' => 1, // no more details as it is a security leackage
'message' => 'Ops! There is an Internal Server Error. Please come back later...'
)
);
}
// SQL query
$stmt = $mysqli->prepare("
INSERT INTO `products_votes`
(`item_id`,`user_finger_print`,`vote_given`)
VALUES
(?, ?, ?)
ON DUPLICATE KEY UPDATE `vote_given`= ?")
// DUPLICATE KEY WILL BE TRIGGERED ONLY IF YOU SET THIS RESTRICTION IN PHPMYADMIN FOR `user_finger_print` and `item_id` columns inside `items_votes` table
$stmt->bind_param("i", $item_id); // first ? (question mark) will be replaced by (i)nteger $item_id
$stmt->bind_param("s", $user_finger_print); // second ? (question mark) will be replaced by (s)tring $user_finger_print
$stmt->bind_param("i", $vote_given); // third ? (question mark) will be replaced by (i)nteger vote_given
$stmt->bind_param("i", $vote_given); // last ? (question mark) will be replaced by (i)nteger vote_given
$stmt->execute();
if ($stmt->errno){
$stmt->close();
respond(
'internal_server_error',
array(
'code' => 2, // no more details as it is a security leackage
'message' => 'Ops! There is an Internal Server Error. Please come back later...'
)
);
}
// close statement
$stmt->close();
// close sql connection
$mysqli->close();
// respond back to user
respond('success');
} else {
respond(
'error',
array(
'message' => 'POST data is missing'
)
);
}
?>
It may have a few errors, as I wrote it in Notepad++ but you should get the logic from it.
HTML code
I removed the id attribute because it was not necessary, and also added data-itemid="XXXXX" attribute to the wrapping parent. I added this information to the wrapping parent to prevent duplicating it for each <i> element.
<div class="item-vote-wrapper" data-itemid="1">
<i class="far fa-thumbs-up fa-1.5x" data-index="0"></i>
<i class="far fa-thumbs-up fa-1.5x" data-index="1"></i>
<i class="far fa-thumbs-up fa-1.5x" data-index="2"></i>
</div>
jQUERY code
I added comments inside code, so please read them.
$('body').on('click', '.far.fa-thumbs-up', function(){
let data_for_ajax = {};
// $(this) is the <i> element that was clicked
let vote = $(this).data('index');
// $(this).parent() - is the parent of this <i> element, meaning div with class "item-vote-wrapper"
let item_id = $(this).parent().data('itemid');
// Append values to our object
data_for_ajax['item_id'] = item_id;
data_for_ajax['vote_given'] = vote;
$.ajax({
url: "betrayal.php",
method: "POST",
data: data_for_ajax,
// Server returns a status code between 200 and 399
success: function(response) {
// This code will execute only when the request succeded
console.log(response);
},
// Server returns a status code between 400 and 505
// Please read this document to understand them better: https://www.tutorialspoint.com/http/http_status_codes.htm
error: function(jqXHR, textStatus, errorThrown){
console.log("An error occured. Request ended with status: " + textStatus);
console.log(jqXHR);
alert('An error occured. Open DEV tools by pressing F12 and check Console tab');
}
});
})

In the html , id of i isn't unique.
In php u don't update with insert because u generate a new id(AI), u can put a query and after a if , if find a record using a update query , dont find use a insert

Related

Issue on php mysql toggle swtich change

I am newbie to php and mysql. Using Cordova plugin for visual studio 2015 i have created a simple mobile application in which a user selects a serial number and on selecting it data is showed on the chart. Also there is a toggle switch button through which i can change the state of the switch from on-off or off-on. While changing the state the data is inserted into DB.
Now, i want is to do is that whenever i select a serial number the last state of the toggle should be shown on the switch. Means if the user has previously change the state from on to off for a particular serial number than this change should be shown if the same serial number is selected again.
For this i have done the following
My PHP CODE
require_once('config.php');
$dsn = $_REQUEST['Device_Serial_Number'];
$cmd_Name = isset($_REQUEST['Command_Name'])?$_REQUEST['Command_Name']:"";
$sqlFet = "select ADC.Server_Device_Command_ID , ADC.Device_ID ,
ADC.Server_Command_ID as Server_Command_ID, ASD.Command_Name
from ADS_Server_Device_Command ADC
inner join ADS_Server_Command ASD on adc.Server_Command_ID = asd.Server_Command_ID
inner join ADS_Device dsn on adc.Device_ID = dsn.Device_ID
where dsn.Device_Serial_Number = '$dsn'
order by adc.Server_Device_Command_ID desc LIMIT 1";
$result = mysqli_query($con,$sqlFet);
//print_r($result);
echo json_encode( (array) $result->fetch_object());
mysqli_close($con);
echo $cmd_Name;
My html(ajax) code
$.ajax({
method: "GET",
url: "http://localhost:Port/server/toggleFetch.php",
data: { Command_Name: tData , Device_Serial_Number: selectedVal },
success: function (data) {
var dt = $.parseJSON(JSON.stringify(data));
console.log(dt);
if (dt.Command_Name == "On") {
$("#cmn-toggle-7").prop('checked', true);
} else if (dt.Command_Name == "Off") {
console.log('else');
$("#cmn-toggle-7").prop('checked', false);
}
},
error: function () {
toastr.success('Data not fetched', '', { timeOut: 2000 })
}
});
Now whenever i run the app this part of code executes and it always goes to the else part. i.e if my serial number's last state is On it shows me off as shown in the below image
In above image you can clearly see that the command_Name = on but still it goes to else part.
I must be doing something wrong, and that i don't know.
Any help would be highly appreciated.

How to increment value in database?

I have a table called Stats. Stats has 4 columns: id, ip, count, and date. count is the number of clicks the ip has clicked on the ads on my site. Each time the user clicks on an ad, their count number will increase by 1. How do I increase their count number by 1 and update that in the database? Here is my code and it's not working for some reason. When I click on the div, it doesn't refresh...so the whole block of code isn't executing. Note that I've already captured the user's ip when they entered my site, this is the part where if they clicked my ad, the count is incremented and updated in the database.
<script>
$(document).ready(function()
{
$("#ad").click(function()
{
<?php
$queryTot = "SELECT `count` AS total FROM Stats WHERE ip = '$ip'";
$resultTot = $db->query($queryTot);
$data = $resultTot->fetch_assoc();
$count = $data["total"] + 1;
$insert = $db->prepare("UPDATE Stats(count) WHERE ip = '$ip' VALUES(?)");
$insert->bind_param('i', $count);
$insert->execute();
$insert->close();
?>
location.reload();
})
})
</script>
There is a lot of points to consider in your answer.
But very possibly you should use an AJAX solution to do it, and avoid every SQL queries in your HTML pages, because keeping SQL queries there definitely is not a good pratice, according all basic security and code maintenance POVs.
It is not possible to re-write your code here rightly without knowing your project structure, or even your idea, and you must take this answer as an important start point.
Basically, you must define in your server-side application a method which returns pure data, in a JSON format for example, and then use AJAX to access this method according an event (a click, for example), receive its response and modify your client-side, probably with jQuery and JS.
I hope this answer can help you.
I've written a short example for you that you could continue to build on to accomplish what you need. Here's the basic idea.
HTML
<input type="hidden" id="ip" value="<?php echo $_SERVER['REMOTE_ADDR'];?>"/>
jQuery
var ip = $('#ip').val();
$(document).ready(function(){
$('#ad').on('click',function(){
$.ajax({
type: 'POST',
url: 'ajaxUpdateDatabase.php',
data: 'ip='+ ip,
success: function(response){
console.log(response)
//send the user to the ad's page here for example
//you could use location.href='url-to-add-here';
//or if you really want to reload the page for a reason I fail to understand, use location.reload();
}
});
});
});
PHP (ajaxUpdateDatabase.php)
//please note that using UPDATE requires that there is previously an entry with this IP address.
//example using PDO...
$sql = 'SELECT * FROM stats WHERE ip = ?';
$stmt = $pdo->prepare($sql);
$stmt->execute(array($_POST['ip']));
if($stmt -> rowCount() > 0){
$sql = 'UPDATE stats SET count = count + 1 WHERE ip = ?';
$stmt = $pdo->prepare($sql);
$stmt->execute(array($_POST['ip']));
}
else{
//ip-address was not found in the database
//insert query goes here
}

Keep getting logged out after updating the database in PHP

I recently posted a question about deleting multiple rows in the database and basically re-used the code to update multiple rows in the database, but now I am having issue once the database has been updated and the page refreshes it keeps loggin me out an I'm not sure why.
Here is the ajax:
function editUser(){
var url = 'edit-user.php';
var ids = document.getElementById("edit-user-id").value;
var role = document.getElementById("role").value;
var status = document.getElementById("accountStatus").value;
var data = 'userID=' + ids.toString() + '&role=' + role + '&status=' + status;
$.ajax({
url: url,
data: data,
cache: false,
error: function(e){
alert(e);
},
success: function () {
var selects = $('#users-table').bootstrapTable('getSelections');
ids = $.map(selects, function (row) {
return row.id;
});
$('#users-table').bootstrapTable('refresh', {
silent: true
});
location.reload();
}
});
}
And here is the PHP:
require("../config.php");
try{
$role = $_GET['role'];
$status = $_GET['status'];
$ids = array($_GET['userID']);
$inQuery = implode(',', $ids);
$query = 'UPDATE users SET role = :role, account_status = :status WHERE user_id IN ('.$inQuery.')';
$query_params = array(
':role' => $role,
':status' => $status
);
$stmt = $db->prepare($query);
$stmt->execute($query_params);
// Set variable message of affected rows
$count = $stmt->rowCount();
$user_updated = ''.$count.' user(s) updated successfully.';
$_SESSION['user_updated'] = $user_updated;
} catch (Exception $e){
$error = '<strong>The following error occured:</strong>'.$e->getMessage();
$_SESSION['error'] = $error;
}
I tried changing cache: true, but that didn't work. Again, I do not want to be logged out. Any ideas what I am doing wrong?
EDIT: I have narrowed it down to only happen when the page refreshes. I removed this piece of code location.reload(); from the ajax call and it does not redirect me back to the login page, but if i hit F5 or click refresh it logs me out.
This is too long for a comment:
Nothing is jumping out at me that would cause you to lose what is set in the $_SESSION['user']. Try dumping $_SESSION on each page just to keep track of it and disable the redirect for now (just put an error message or something). You can dump the array like so:
print_r($_SESSION);
Do you also know your prepared statement is broken? I don't see the point of the array or the implode for $ids and $inQuery. It should be something like:
$stmt = $db->prepare(
'UPDATE users
SET role = ?, account_status = ?
WHERE user_id = ?'
);
$stmt->execute(array($_GET['role'], $_GET['status'], $_GET['userID']));
There is no point in using IN if you only have one entry. You also aren't protecting your query from anything because you are still inserting the values into the prepare statement.
It appears that I needed to move session_start() to the top of the config.php file to make sure that it is the very first thing called on the page. Everything seems to be working ok right now.

Retrieve data from MySQL database after jQuery autocomplete

I have implemented on my site the jQuery autocomplete function which works well. However, I would like to use the result from the autocomplete to retrieve the selected person's telephone number from the database.
The database structure is this;
id | name | type | tel | mobile | email | level
===============================================
1 | John Smith | judge | 01234 567890 | 07812 345678 | jsmith#example.com | BS Two Star
Here is my updated code so far
Autocomplete function
<script>
jQuery(document).ready(function($) {
$('#inputChiefJudge').autocomplete({
source:'lookups/shows-sj-searchforjudge.php',
change: function (event, ui) {
$.ajax({
type: POST,
url: 'lookups/shows-sj-findtel.php',
data: 'id='+ id,
success: function(data) {
details = $.parseJSON(data);
$('#inputChiefJudge').text("hello");
$('#chiefjudgetel').text(details);
},
});
},
minLength:2});
});
</script>
lookups/shows-sj-findtel.php
<?php
include("config.php");
mysql_connect ($DbHost, $DbUser, $DbPass);
mysql_select_db ("equilive_manager");
$id = $POST["id"];
$result = mysql_query("SELECT tel, mob FROM officials WHERE id='{$id}'");
$judgerow = mysql_fetch_array($result, MYSQL_ASSOC);
$contactdetails[] = array(
'tel' => $row['tel'],
'mob' => $row['mob'],
);
echo json_encode($data);
flush();
?>
lookups/shows-sj-searchforjudge.php
<?php
// if the 'term' variable is not sent with the request, exit
if ( !isset($_REQUEST['term']) ) exit;
// connect to the database server and select the appropriate database for use
include("../config.php");
mysql_connect ($DbHost, $DbUser, $DbPass) or die ('I cannot connect to the database because: ' . mysql_error());
mysql_select_db ("equilive_manager");
// query the database table for name that match 'term'
$term = mysql_real_escape_string($_REQUEST['term']);
$rs = mysql_query("SELECT id, name, level FROM officials WHERE name LIKE '%{$term}%' ORDER BY name LIMIT 0,10");
// loop through each name returned and format the response for jQuery
$data = array();
if ( $rs && mysql_num_rows($rs) )
{
while( $row = mysql_fetch_array($rs, MYSQL_ASSOC) )
{
$data[] = array(
'label' => $row['name'] .', '. $row['level'],
'value' => $row['name'],
'id' => $row['id'],
);
}
}
// jQuery wants JSON data
echo json_encode($data);
flush();
Thanks in advance,
Craig
You have one issue at least in the code, which is that in getChiefJudgeContactDetails() you're mixing javascript with php. Mixing the two works fine if it's the first time you output a page and the code is on a PHP page. But if you're expecting the javascript to run PHP code every time a change event is triggered from the auto-complete, then that won't work.
Use the select event as others have stated, inside that, make a ajax request to a similar end point as your autocomplete but send it the value of your option (e.g. the ID value 2). Then use SQL in a PHP script to fetch the row for that id and return it as a json object. Parse the result and update UI in the jquery ajax call result handler.
update:
Change your autocomplete to look like this
<script>
jQuery(document).ready(function($) {
$('#inputChiefJudge').autocomplete({
source:'lookups/shows-sj-searchforjudge.php',
select: function (event, ui) {
$.ajax({
type: POST,
url: 'lookups/shows-sj-findtel.php',
data: {id:id},
success: function(data) {
details = $.parseJSON(data);
$('#inputChiefJudge').text("hello");
$('#chiefjudgetel').text(details);
},
});
},
minLength:2});
});
</script>
Instead of using the change option of the autocomplete, use select (as stated by other answers to your question). Also, instead of using a string ("id="+id) as your data, use a js object ({id:id}). jquery will handle serializing it correctly before sending to the server, the result being that it actually shows up as a post variable in your php script.
Also, as more of a side note, I would suggest looking into using the PDO driver (http://www.php.net/manual/en/pdo.prepare.php) to access your database instead of using the mysql_* commands. It's object oriented and also automatically provides safety features that are not available in the old commands, such as prevention of SQL injection attacks.
You can do it in select option of autoComplete.
All you need to do is send new ajax request to get selected person number.
select: function (event, ui)
{
//make $.ajax request and send selected value.
//you can send selected value using => ui.item.value
}
You should use the "select" event of the autocomplete:
http://api.jqueryui.com/autocomplete/#event-select

Simple PHP chat quirk

I've been working on a quick and simple jQuery/PHP chat to put in my website for the visitors to communicate. I've extimated peaks of 200 simultaneous website users (connected users) with at most 10-20 people actually chatting.
Here's the quirk:
As I experienced already twice (thought it seems to be rather an unlikely event more than something happening after you perform something specific) the chat happens to load multiple messages which have already been red and display them.
Trying to keep the chat system as simple as possibile I came up with this code:
HTML CODE:
<div class="chat">
<ul class="chat">
<li class="chat" >
<h5 class="chat">Date</h5>
<h6 class="chat">Time</h6>
<h4 class="chat">User</h4>
<br/>
<q class="chat">Message</q>
</li>
</ul>
<input class="chat" placeholder="write something..."/>
</div>
As you can see I put a placeholder li element for the jQuery to take and use as a snippet to create new li elements with the actual messages and prepend them inside the ul element.
jQuery CODE:
Sending messages:
$(document).ready(function(){
chatSnippet = $('ul.chat').html(); // here chatSnippet is a global variable
$('ul.chat').html('');
$('input.chat').change(function(event){// Send your message
message = $(this).attr('value');
// first thing I perform an asynchronous POST to the receiving php script
$.post(
'php/chatRec.php',
{
user : currentUser,
message: message,
}
);
// meanwhile I add a new li element to the chat html with the content just submitted
date.setTime(event.timeStamp);
hours = ''+date.getHours();
if(hours.length < 2) hours = '0'+hours;
minutes = ''+date.getMinutes();
if(minutes.length < 2) minutes = '0'+minutes;
day = ''+date.getDate();
if(day.length < 2) day = '0'+day;
newChatMessage = chatSnippet.replace('Date', ''+day+' '+months[date.getMonth()]);
// here months is an array with the months names (in italian)
newChatMessage = newChatMessage.replace('Time', ''+hours+':'+minutes);
newChatMessage = newChatMessage.replace('User', connectedUser);
newChatMessage = newChatMessage.replace('Message', message);
$mess = $(newChatMessage);
$mess.hide().prependTo('ul.chat').fadeIn(500);
$(this).attr('value','');
});
refreshChat(''); // this function retrives new messages from the DB
// Here I perform a void refreshChat call so I'll get all the messages in the DB regardless from the currentUser (at page refresh)
});
Receiving messages:
// This code is placed outside (before) the .ready function
function refreshChat(user){// Receiving messages
$.post(
'php/chatInv.php',
{
user : user,
token: lastMessage // this variable contains the token of the last red message
},
function(data){
receivedMessages = jQuery.parseJSON(data);
for(message in receivedMessages){
message = receivedMessages[message].Message;
date = receivedMessages[message].Day.split('-');
time = receivedMessages[message].Time.split(':');
newChatMessage = chatSnippet.replace('Date', ''+date[2]+' '+months[parseInt(date[1])-1]);
newChatMessage = newChatMessage.replace('Time', ''+time[0]+':'+time[1]);
newChatMessage = newChatMessage.replace('User', receivedMessages[message].Sender);
newChatMessage = newChatMessage.replace('Message', message);
$mess = $(newChatMessage);
$mess.hide().prependTo('ul.chat').fadeIn(500);
lastMessage = receivedMessages[messages].token;
}
nextRefresh = setTimeout("refreshChat('"+currentUser+"')",2000);
// When I'm done I set a timeout of 2 secs and then perform another refresh
}
);
}
PHP CODE:
Receive a new message (I think the issue is in here):
mysql_connect("localhost", "root", "root") or die(mysql_error());
mysql_select_db("chat") or die(mysql_error());
$characters = array('0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
$token = $characters[rand(0,61)].$characters[rand(0,61)].$characters[rand(0,61)].$characters[rand(0,61)].$characters[rand(0,61)];
$all_Msgs = mysql_query("SELECT * FROM Messages ORDER BY ID");
$prev_Msg = array('ID' => 1 , 'Sender' => $_POST['user'], 'Message' => $_POST['message'], 'Day' => date("Y-m-d"), 'Time' => date("H:i:s"), 'token' => $token);
while($Msg = mysql_fetch_array($all_Msgs)){
$update_success = mysql_query("UPDATE Messages SET Sender='".$prev_Msg['Sender']."', Message='".$prev_Msg['Message']."', Day='".$prev_Msg['Day']."', Time='".$prev_Msg['Time']."', token = '".$prev_Msg['token']."' WHERE ID=".$Msg['ID']);
$prev_Msg = $Msg;
}
Basically what I do here is receive the new post message, generate a token and an array element (which is itself an array) containing the new entered datas, done this I perform a seuqence of UPDATE statements on a fixed size SQL table overriding the new datas on the first record and then overriding each record with the previous one (so that the last record will be finally lost).
Sending messages:
mysql_connect("localhost", "root", "root") or die(mysql_error());
mysql_select_db("chat") or die(mysql_error());
$receiver = $_POST['user'];
$token = $_POST['token'];
$all_Msgs = mysql_query("SELECT * FROM Messages ORDER BY ID");
$newMessages = array();
while($Msg = mysql_fetch_array($all_Msgs)){
if($Msg['token'] == $token) break;
if($Msg['Sender'] != $receiver) array_unshift($newMessages,$Msg);
}
echo json_encode($newMessages);
So I send the client the JSON encode of an array of all the records in the DB inserted after the last known message and whose author was not the querying client.
My suspects:
I came to the conclusion that when the message reception (server side) is being performed there is a time span when each message is taken from the DB, if a refresh is being performed in the meanwhile the message is not found and if that message was the one we were looking for as the last red message then the server will just select all the messages in the table and send them back.
The result is you see a bunch of messages you already red without your messages in between (cuz they were added to the view client side and the server script doesn't send you back your own messages)
Stated that:
I don't care if the messages aren't exactly in the actual insertion order: let's say A and B are chatting, the actual real messages order is BAB, but A may se the order ABB for his view is immediatly updated at input time (this helps me keep a 'fast-realtime' feel)
I don't care if some message is lost (like if it falls over the fixed DB table edge before someone can read it)
At this time I don't care much about actual efficency, speed and optimization
I know I should probalby handle the message insertion differently adding the new record and then updating the IDs only and delete the last record out. But if possible I'd like to keep this UPDATE-only fashion.
do you think my interpretation of the problem is right?
If not: what would then be the cause? / how can I fix that?
If yes: how can I fix that easily?
If the actual fix is rather complex: how actually likely to happen would be this quirk in a 10-20 users chat?
Thanks
I noticed this when I worked on a chat code too, the solution is to store the last message ID (set as an Auto Increment field in MySQL) in a session and search the database for messages where the ID is higher than that, rather than use the time() function.
if (!$_SESSION['message_id']]) {
// if there isn't a message_id, select the last seven entries in the message list
$sql = "SELECT messages.message_id, messages.message, users.username FROM (SELECT * FROM messages, users user.user_id = messages.user_id ORDER BY message_id DESC LIMIT 7) as new_tbl ORDER BY message_id ASC";
} else {
// if there is a message_id, select the messages sent since the last entry
$sql = sprintf("SELECT messages.message_id, messages.message, users.username FROM messages, users WHERE user.user_id = messages.user_id message_id > '%d'", $_SESSION['message_id']);
}
$data = array();
$query = mysql_query($sql);
while ($row = mysql_fetch_array($query)) {
// build the data array from the mysql result and set the message_id session to the id of the last message
$data[$i] = array('user' => $row['username'], 'message' => $row['message']);
$_SESSION['message_id'] = $row['message_id'] > $_SESSION['message_id'] ? $row['message_id'] : $_SESSION['message_id'];
$i++;
}
Obviously you'd need to escape the session!
If there isn't a message_id session, it loads the last 7 messages from the table (ordered descending, then orders those messages in ascending order). If there is a message_id session, it loads new messages.
In the while loop, it builds a data array (I send it to my script as JSON) and sets the message_id session as the message_id row, with a failsafe check to make sure the message_id session doesn't end up being lowered.
The SQL implies that you have a table of users with the user_id and username, and a table of messages with a user_id, message_id and message.

Categories