问题
So I am setting up a PHP site like Flickr, except these photos can be rated. So I am setting up the rating system in AJAX so that when the user clicks on a star to rate the photo, the stars on the photo reflect the new rating computed. (photo's rating is depicted by 10 stars that are either yellow or blank).
The stars react how they should when you mouseover them they light up up to where your cursor is pointing. When you click a star, the corresponding value is logged in photo_rating table and updated in photo table. Then the newly computed rating is reflected through the stars.
UPDATE It pretty much all boils down to this call not returning the correct rating, it is still giving back the original rating for the photo when the page loaded. And not the new rating that has been saved in the DB through AJAX (even though the value in the DB has been changed)
onmouseout="stars_current_rating(<?php echo Photograph::find_by_id($_GET['id'])->rating;?>);"
HERE is the problem: After clicking, the stars are updated with the correct newly computed rating. But when I mouse back over the stars, then mouse out. The stars rating go back to the original rating they were at when the user first loaded the page. So the rating that is being reflected is what was pulled from the db when the page loaded. Not the value that is now residing in the ratings column of the photo table.
Originally I was using $photo->rating but I figured since I used AJAX that value wasn't getting computed again. So then I switched the onmouseout to call Photograph::find_by_id($_GET['id'])->rating; which should be instantiating a new object by checking the database from its id and getting the current rating. But even this doesn't seem to be giving me the updated value.
I have used firebug, firePHP and am doing the profiling to see exactly what values are being passed into these methods when I click and mouse over them. For some reason the new rating is only being pulled from the DB, right after the user votes the new value is shown. But any time after when you mouse over the stars they go back to what they were when you loaded the page. Is there anyway I can get that updated value to be reflected in the code below without refreshing the entire page? If I refresh the page then it works, but the whole point was to do it all through AJAX so the whole page didn't have to be reloaded...
This is how the calls are set up on the page.
<div id="rating" class="rating">
<?php
for($i=1; $i<11; $i++){
?>
<a id="rating_star<?php echo $i ?>" href="#" onmouseover="decide_rating(<?php echo $i ?>);"
onmouseout="stars_current_rating(<?php echo Photograph::find_by_id($_GET['id'])->rating;?>);"
onmousedown="set_rating(<?php echo $photo->id.", ".$i ?>);">
<img id="star<?php echo $i ?>" class="rating_star" src=
<?php
// keeps stars always set at rating for photo when not being rated
if($photo->rating >= $i){
echo "images/assets/rating_star.png";
} else {
echo "images/assets/rating_star_off.png";
}
?>
/>
</a>
<?php } ?>
</div>
Here is my ratings.js file that contains the 3 used above for onmouseactions (I understand that decide_rating and stars_current_rating is the same code, they were different when I had them set up for a different page, where the logic was different, but the updating wasn't working so I went back to this simpler example)
// Makes stars light up, up to where you have your cursor pointing
// pass in current rating of star hovered over
function decide_rating( r ){
for(var i=1; i<=10; i++){
var star = document.getElementById("star"+i);
if(i<=r){
star.src = "images/assets/rating_star.png";
} else {
star.src = "images/assets/rating_star_off.png";
}
}
return false;
}
// defaults the stars to light up with the current rating
function stars_current_rating(r){
for(var i=1; i<=10; i++){
var star = document.getElementById("star"+i);
if(i<=r){
star.src = "images/assets/rating_star.png";
} else {
star.src = "images/assets/rating_star_off.png";
}
}
return false;
}
function set_rating(pid, rating){
if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
} else {// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function(){
if(xmlhttp.readyState==4 && xmlhttp.status==200){
// no message to be displayed
//document.getElementById("message").innerHTML=xmlhttp.responseText;
// for this to work ratings.js must be before this file when linked to
// set_ratings is echoing the $photo->rating for the calling pid
// as the xmlhttp.responseText
stars_current_rating(xmlhttp.responseText);
}
}
xmlhttp.open("GET","../includes/set_rating.php?pid="+pid+"&rating="+rating,true);
xmlhttp.send();
}
I have this set up with 2 tables in my database. One is photo_rating which has the user_id, photo_id, and rating. The photo table also has a column for its current rating (which is computed as the average of the corresponding rows in photo_rating).
Below is the set_rating.php file that the AJAX is calling
<?php
// This file is called by the ratings.js function set_rating
// echo output is used by to update the current rating of the
// photo after it has been saved in the database
require_once('initialize.php');
// create new photo rating and set up attributes
if(isset($session->user_id)){
$pr = new PhotoRating( $_GET['pid'], $session->user_id, $_GET['rating']);
} else {
$pr = new PhotoRating( $_GET['pid'], NULL, $_GET['rating']);
}
// save the rating in photo_ratings table
// and the rating in the photograph table
$pr->save_rating_update_photo();
echo Photograph::find_by_id($_GET['pid'])->rating;
?>
And these are the functions that are being called within PhotoRating
// saves the current photo_rating into the database
// Then calls update photo rating up update the
// rating in the photo database table
function save_rating_update_photo(){
if($this->save()){
// Success
$message = "entry saved in photo_rating";
//$session->message("{$photo->filename} was uploaded");
} else {
// Failure
$message = join("<br />", $this->errors);
}
$this->update_photo_rating();
}
function update_photo_rating(){
$photo = Photograph::find_by_id($this->p_id);
$newRating = ($this->rating + self::sum_all_ratings($this->p_id))/($this->count_all()+1);
$photo->rating = $newRating%10;
if($photo->save()){
// Success
} else {
// Failure
$message = join("<br />", $photo->errors);
}
}
Any insight would be appreciated, I have been banging my head on the desk since I started testing this out late yesterday. I understand I can just reload the page, but I feel like that negates the efficiency of having this all happen through AJAX.
回答1:
After briefly looking at your question/issue and checking the source of your live site, here's what I'd do:
In ratings.js, at the top, add:
// Global variable to hold the current rating
var star_rating = 0;
Change stars_current_rating(r)
into:
// defaults the stars to light up with the current rating
function stars_current_rating(){ // no "r"
for(var i=1; i<=10; i++){
var star = document.getElementById("star"+i);
if(i<=star_rating){ // !!!
star.src = "images/assets/rating_star.png";
} else {
star.src = "images/assets/rating_star_off.png";
}
}
return false;
}
In set_rating(pid, rating)
, change current row 48 into this:
star_rating = xmlhttp.responseText;
stars_current_rating();
In photo.php
, in each a
, change the onmouseout
to:
onmouseout="stars_current_rating();"
The reason why you don't get the result you expect is that the value of the rating is hard coded into the onmouseout
function call. Having a variable in javascript that changes with the xmlhttp response is crucial.
For this to work when page loads as well, make a script part in photo.php that sets the star_rating
properly (via <?php echo Photograph::find_by_id($_GET['id'])->rating;?>
)
Hope this helps!
来源:https://stackoverflow.com/questions/12751568/updating-and-displaying-the-most-recent-record-in-db-after-ajax-updates-the-va