I am working on a small news website and try to understand and implement Long Polling. I found the following example what actually comes very near to what my requirements are:
I have been searching and trying all kind of things all day but so far without any success. I was wondering if there is no way to pause the waitForMsg() function once the while loop has deliverd the result set?
If your news items had a timestamp column for the time they are added to the system, you could have your AJAX code submit the timestamp of the last request and simply return anything newer than that. Alternatively, if your news table uses sequential IDs, keep track of the last ID and poll for anything with a greater ID.
Hi fretburner. Thanks for the reply I have solved the repeated messages issue server side, by doing a update query on the isNew status within the while loop. It indeed shows the messages just one time but somehow the function is stil executing because after a while a scroll bar appears in the browser
I have a timestamp in the table news, called add_date, So how do I integrate that in the function as it is? Because if that solves the former issue I would be more than happy
How and where do I poll for a greater vallue.
I have another question if I may. In a lot of examples I see the use of echo json_encode($data); or echo $json;
Like I said in the opening post this is all very new for me and I really try and like to understand how this all works but it is confusing me somehow. Any advise or tutorial would be really appreciated.
Fair enough, in this case the problem is that when there is no news returned from the server your polling function is still adding a div to the page. You just need to add a check to see if anything was returned from your AJAX call:
json_encode is useful when you want to return arrays or objects to your JS, rather than plain text or HTML. It’s quite common for APIs to return data as JSON, and for apps to generate the HTML client-side.
Here’s how your code might look if you were returning JSON and using the timestamp method of checking for updates:
Hi fretburner. Again thank you for your reply and explanation, it is realy appreciated.
Your explanation is actually quite logical, so I have changed the success function to your suggestion, but for some reason the scroll bar still appears after a certain amount of time. Next to the div that is appended what could be the reason?
I need to have a look at this in more detail because I don’t want to simply copy this and use it, I also would like to understand the functionality.
It sounds like maybe your PHP script is returning something that evaluates as truthy. Is there more to news.php than you previously posted, or that was the whole script?
Hi fretburner. maybe I should have mentioned this. At the very top of news.php I have another query which actually holds the same values, but Is only used to extract certain variables which I need on another pages, so this is the complete news.php
I now have added the update query to the while loop so now the complete news.php looks like this:
// This query I use to set certain variables which I need elseware
$qry = "SELECT MIN(news_id) AS news_id
, sender_id
, add_date
FROM news
WHERE isNew = 1";
$stmt = $pdo->query($qry);
$vars = $stmt->fetch();
$news_id = $vars['news_id'];
$sender_id = $vars['sender_id'];
$add_date = $vars['add_date'];
// With this query I fetch the oldest news item with status isNew = 1 and all other news items from this sender
$sql = "SELECT news_id
, sender_id
, news_content
, add_date
FROM news
WHERE sender_id =
(SELECT sender_id
FROM news
WHERE add_date =
(SELECT MIN(add_date)
FROM news
WHERE isNew = 1))
ORDER
BY add_date DESC";
$result = $pdo->query($sql);
$news_id = 0;
while($row = $result->fetch()) {
if ($row['news_id'] > $news_id) {
$news_id = $row['news_id'];
}
echo "<p>{$row['news_content']}</p>";
$qry = "UPDATE news SET isNew = 0 WHERE news_id = ?";
$stmt = $pdo->prepare($qry);
$stmt->execute(array($news_id));
}
I still have the issue with the scrollbar though. and as you can see is it just a paragraph I use for the news_content. I still have no idea what could be the reason. And then there is another thing I need to solve. Since I fetch the oldest news item with staus isNew = 1 there will be quite possibly be more recent news items with status isNew = 1 but then from other senders. Like the structure is now, if there are more recent news items with status isNew = 1 from other users, they are loaded as well, while I am looking for a way to limit the listing with news items per sender per time. What would be in your opinion the best way to accomplish that? I have a comment field where other users can leave a comment, could that be a possibility?
I’ve got a couple of suggestions regarding the updated news.php script:
Running the update query within the while loop is going to result one DB query per news item. It’d be more efficient to add each news_id to an array and then update the records in a single query, outside of the loop:
$news_ids = array();
while($row = $result->fetch()) {
$news_ids[] = $row['news_id']; // Add the ID to an array
echo "<p>{$row['news_content']}</p>";
}
// Update all new records at once
$qry = 'UPDATE news SET isNew = 0 WHERE news_id IN (' . implode(',', $news_id) . ')';
$pdo->query($qry);
Note that it’s more complicated to do a prepared statement using the WHERE … IN () clause, and as the values are record IDs from the DB it’s safe enough to use a plain old query instead.
You can rewrite the main query to avoid the need for one of the nested sub-queries:
SELECT news_id, sender_id, news_content, add_date
FROM news
WHERE sender_id =
(SELECT sender_id
FROM news
WHERE isNew = 1
ORDER BY add_date ASC
LIMIT 1)
ORDER BY add_date DESC
The news items are listed under a comment form so I needed a listing per sender, so for me this is working just fine.
Like I said above the with the while(display) loop I fetch the messages from just on sender so I only need to update the isNew status from the last message from that sender.
As explained would I like to display a listing with messages per sender, rather then displaying all messages with status isNew = 1; With the above table values along with the query as mentioned before I fetch all news items from sender_id 1 (news_id’s: 7, 4 and 1 DESC)
WHERE sender_id =
(SELECT sender_id
FROM news
WHERE add_date =
(SELECT MIN(add_date)
FROM news
WHERE isNew = 1))
With the above table values the if/else statement in the AJAX method::
was working since there were still 2 news_id’s in the database with status isNew = 1 and this was reason as well why the scroll bar was appearing after a while. But after commenting to those 2 messages (id’s 8 and 9) as well and updating their isNew status to 0 the if/else statement won’t function any longer. It will only function as long as there are news_id’s with status isNew = 1 before the last one was updated to 0.
I hope this makes any sence.
How do I need to change the AJAX method and/or Query to accomplish where I am after.
OK, so lets say that on first load the server returns records 1, 4, and 7, and then sets isNew to 0 for record 7. Should the next call to the server (via AJAX) return the records for sender 2? Or it should only be looking for new records from the current sender (1)?
Yes after a comment (form) to the last messages of sender 1 is submted it should return indeed all records from sender_id 2 since the query is looking for the oldest record with status isNew = 1.
SELECT MIN(verzonden) AS verzonden
FROM berichten
WHERE isNieuw = 1
and that is in the example table indeed record 8 from sender 2 etc etc etc
OK, I think I follow… let me just check something with you: you say that after a form is submitted, then the server should return records for the next sender - what about if the user opens the page, but doesn’t reply (i.e. doesn’t submit the form)? Then the AJAX call should only return any new messages from the current sender (sender_id 1, in our example) that have been sent since the page was loaded?