AJAX star rating plugin
Features
- Multi user, multi model rating
- Just one element in your views
- Seamless integration with AJAX
- Prototype and jQuery support
- Cross browser compatibility
- Fallback for disabled javascript
- Various configurations
Requirements
- CakePHP 1.2
- Prototype or jQuery framework
- User id stored in session for secure rating
Demonstration
A demo can be tested and downloaded athttp://ratingdemo.schneimi.spacequadrat.de/
Download
http://www.wuala.com/mystic11/public/rating2.2.zip Or get the code on the following pages.Installation and Use
1) Make sure you meet the requirements above. For the download and integration of a javascript framework, please visit http://www.prototypejs.org/ for Prototype and http://jquery.com/ for jQuery.
2) Extract the plugin, including the subfolder rating, to your app plugins folder app/plugins.
3) Copy the rating/config/plugin.rating.php to your app configs folder app/config and change the settings to your desire. It is recommended to let Rating.showHelp set to true until everything works.
4) Apply the install.sql to your database to create the ratings table.
Download code
CREATE TABLE `ratings` (
`id` int(11) unsigned NOT NULL auto_increment,
`user_id` char(36) NOT NULL default '',
`model_id` char(36) NOT NULL default '',
`model` varchar(100) NOT NULL default '',
`rating` tinyint(2) unsigned NOT NULL default '0',
`name` varchar(100) default '',
`created` datetime default NULL,
`modified` datetime default NULL,
PRIMARY KEY (`id`),
KEY `rating` (`model_id`,`model`,`rating`,`name`)
);
5) Load the plugin javascript and css files into your layout. Replace [your_framework] with prototype_min or jquery_min depending on the framework you use.
View Template:
Download code
<?php
echo $javascript->link('/rating/js/rating_[your_framework]');
echo $html->css('/rating/css/rating');
?>
6) For full model integration in your app, apply the following relation to your models. (replace [name_of_your_model])
Model Class:
Download code
<?php
var $hasMany = array('Rating' =>
array('className' => 'Rating',
'foreignKey' => 'model_id',
'conditions' => array('Rating.model' => '[name_of_your_model]'),
'dependent' => true,
'exclusive' => true
)
);
?>
7) If you set Rating.saveToModel to true, then add the defined Rating.modelAverageField and Rating.modelVotesField to all models you want to rate. To do that you can use the following SQL statements (replace [your_table], [Rating.modelAverageField], [Rating.modelVotesField]).
Download code
ALTER TABLE [your_table] ADD (`[Rating.modelAverageField]` decimal(3,1) unsigned default '0.0');
ALTER TABLE [your_table] ADD (`[Rating.modelVotesField]` int(11) unsigned default '0');
If the plugin shows the fields are still missing, try to clear the model cache of your app at app/tmp/cache/models.
8) You can change the styles of the rating element in the css file rating/vendors/css/rating.css.
9) Finally you can place the rating element in your views as follows. (replace [name_of_your_model] and [id_of_your_model])
Default rating element for one model id
View Template:
Download code
<?php
echo $this->element('rating', array('plugin' => 'rating',
'model' => '[name_of_your_model]',
'id' => [id_of_your_model]));
?>
Default rating element for one model id
If you want to have different ratings for one model id like sound and picture of a movie, you can use the additional name parameter.View Template:
Download code
<?php
echo $this->element('rating', array('plugin' => 'rating',
'model' => '<name_of_your_model>',
'id' => <id_of_your_model>,
'name' => 'sound'));
echo $this->element('rating', array('plugin' => 'rating',
'model' => '<name_of_your_model>',
'id' => <id_of_your_model>,
'name' => 'picture'));
?>
Individual configuration of a rating element
Sometimes you want to use more than one style of rating elements in your app. That can be reached with the 'config' parameter and different config files in 'app/config'. Just clone the original 'plugin.rating.php' and give it a different name, which you then pass to the element.View Template:
Download code
// uses 'plugin.rating.php' in 'app/config'
echo $this->element('rating', array('plugin' => 'rating',
'model' => '[name_of_your_model]',
'id' => [id_of_your_model]));
// uses 'plugin.rating.style1.php' in 'app/config'
echo $this->element('rating', array('plugin' => 'rating',
'model' => '[name_of_your_model]',
'id' => [id_of_your_model],
'config' => 'plugin.rating.style1'));
// uses 'plugin.rating.style2.php' in 'app/config'
echo $this->element('rating', array('plugin' => 'rating',
'model' => '[name_of_your_model]',
'id' => [id_of_your_model],
'config' => 'plugin.rating.style2'));
Comments
Comment
1 Super Cool!
Thanks
Comment
2 Little Help with Plugin Models
I keep getting model_id does not exist when I use a plugin model. I enter the model as 'Plugin.Model' eg. 'News.Article'
I've confirmed the model_id exists in d database, I've also tested the same code used in rating_controller->view()
$model = 'News.Article';
$modelInstance = ClassRegistry::init($model);
$modelInstance->id = '3ad66b8f-55ce-11de-bb54-9f1eb639fa3a';
var_dump($modelInstance->exists(true));
In my test, it returns true, but in the the rating_controller it returns false.
I'd really appreciate some help. Thanks in advance.
Comment
3 Little Help with Plugin Models
thx for your comment! I tried to reproduce your problem and created a dummy "news" plugin with a model "article" and also put an article with your uuid into the db "articles", but I didn't experience the problem. Your code snippet shows true on top of the ratings_controller->view().
Do you use the latest CakePHP release? Did you test it with the demo? Atm I have no other idea, but I will let you know if I come up with something.
Regards
Comment
4 Little Help with Plugin Models
I just tested it with the demo and I still get the same error. With models in app/models it works well.
I'm using cake 1.2.3.8166.
Here is how I called the rating plugin.
echo $this->element('rating', array('plugin' => 'rating',
'model' => 'News.Article',
'id' => $article['Article']['id'])
);
?>
Thanks for your help.
Comment
5 Little Help with Plugin Models
ok now I got it too and could figure it out. The problem is, that the sanitization encodes the incoming uuid, that's why the model_id is not found. Further more a rating cannot be saved in the ratings table, because the model_id is an integer field.
I will add the support in the next version, so long you can change the model_id from int to varchar and disable the encoding on Sanitize::clean (in view() and save()) as follows:
Sanitize::clean($id, array('encode' => false));
I really didn't think of uuids, so tanks for your comment.
Comment
6 Little Help with Plugin Models
Comment
7 Error when rating...
Warning (512): DbAcl::check() - Failed ARO/ACO node lookup in permissions check. Node references:
Aro: Array
(
[User] => Array
(
[id] => 3
[username] => test
[group_id] => 2
[created] => 2009-07-22 17:35:44
[modified] => 2009-07-22 17:35:44
)
)
Aco: controllers/Ratings/save [CORE/cake/libs/controller/components/acl.php, line 239]
Its seems to have something to do with the DBACl http://book.cakephp.org/view/644/Initialize-the-Db-Acl-tables but cannot really figure it our. Thanks for your help!
Question
8 Error when rating...
Warning (512): DbAcl::check() - Failed ARO/ACO node lookup in permissions check. Node references:
Aro: Array
(
[User] => Array
(
[id] => 3
[username] => test
[group_id] => 2
[created] => 2009-07-22 17:35:44
[modified] => 2009-07-22 17:35:44
)
)
Aco: controllers/Ratings/save [CORE/cake/libs/controller/components/acl.php, line 239]
Its seems to have something to do with the DBACl http://book.cakephp.org/view/644/Initialize-the-Db-Acl-tables but cannot really figure it our. Thanks for your help!
Question
9 re: Error when rating...
I discovered the mistake myself. I did not have the permission to rate, so had to allow the ratings_controller.php to do this by adding:
function beforeFilter() {
parent::beforeFilter();
$this->Auth->allowedActions = array('*');
}
Comment
10 Good Job!
1) Javascript need to be declared before the element instaciation: not a big deal, but i follow the Steve Souders´ performance tips, so, i prefer to declare my javascript on the end of document. Actually, it shows an error if I do this. A solution is simple - instead of call ratingInit directly, do this:
$(window).load(function() {
renderInit( // arguments, etc..
}
So, the code will be executed after document load. If you have a public svn or git repo, i would like to help you with this.
Thx,
Yuri Teixeira.
Comment
11 Re: Good Job!
Maybe something like this:
rating_prototype.js
function ratingInit(element, data, options, config, enabled) {
Event.observe(window, 'load', function() {
...
});
}
rating_jquery.js
function ratingInit(element, data, options, config, enabled) {
$(window).load(function () {
...
});
}
Regards
Comment
12 Re: Re: Good Job!
I currently see no way how to handle that, if you find something working let me know.
Comment
13 Thanks!
I wanted to let you know that I found a little bug.
If you are using a custom primary key field in your model (not "id"), and you set Rating.saveAverageToModel to true, it won't work.
To make it work I just modified line 105 in ratings_controller.php from this:
$avgRating = $modelInstance->field(Configure::read('Rating.modelAverageField'), array($model.'.id' => $id));
to this:
$avgRating = $modelInstance->field(Configure::read('Rating.modelAverageField'), array($model.'.'.$modelInstance->primaryKey => $id));
Thanks again for sharing it! It saved me a lot of work :)
Nahuel
Comment
14 Images Missing
user/rating/image/star-full.png
it now points at:-
user/rating/img/star-full.png
And when I click on a one of the image place holders I get this error:-
Error: The action rating is not defined in controller UserController
I assume I've missed something simple here since everyone else seems to get it working so easily. I'm using cake 1.2.5, and rating 2.0 with JQuery.
Anyone have any ideas what the problem could be ?
Comment
15 Re: Images Missing
Where does the "user/" come from in your case? I thought it is your appRoot, but in this case there shouldn't be the controller error.
Maybe it would help you to download the working demo and setup a similar configuration of your situation.
Comment
16 flash msg
The flash messages work very well too, although as I already have flash messages in my website, I end up with some general flash messages appearing in the ratings boxes instead of in the initial place where I put my $session->flash().
Would you have any suggestions on how to separate my own flash messages (and decide of the place in the view where it gets displayed), from the ratings flash?
Comment
17 Re: flash msg
To solve the flash collisions you simply have to use a flash key, as shown in the following code.
rating\controllers\ratings_controller.php line 258
$this->Session->setFlash(Configure::read('Rating.flashMessage'),
'default',
array('class' => 'rating-flash'),
'rating');
rating\views\ratings\view.ctp line 95
$session->flash('rating');
Because of ajax reloading, the flash message element has to be within the plugin's view and cannot be placed elsewhere in your view. However it may help you to relocate the div.rating-flash via css in the rating\vendors\css\rating.css
Comment
18 Re: Re: flash msg
Works like a charm. Thanks!
Comment
19 lot of thx
very useful...
Bug
20 Problem loading config values - SOLVED
My biggest (only) problem was that I didn't copy the plugin.rating.php config file into /app/config/plugin.rating.php
None of the Rating config values were set. All were NULL. I wasn't expecting to have to copy config files around. I thought I was diligent about reading the docs, but I must have missed something.
If for no other reason that to make me look foolish, could someone post a link to the documentation that says to copy the config file?
In addition, I think there is a 'bug' in the RatingsController where it loads the config file.
Check this out from line 45 in the RatingsController:
// load config
if (Configure::load($config) === false && Configure::read('Rating.showHelp')) {
echo 'Error: The '.$config.'.php was not found in your app/config directory. Please copy it from rating/config/plugin.rating.php';
exit;
}
Is the logic really saying if I can't load the config file AND the config value to display help is true, then display an error message?
It would seem correct to remove the AND showHelp part of the logic. Yes?
Comment
21 Re: protoaculous
find ich toll, dass du im Rahmen der FH die Möglichkeit hast CakePHP zu verwenden.
Das Plugin hat als Grundvoraussetzung dass entweder Prototype oder jQuery geladen ist, steht auch oben unter Requirements. Ist aber zugegeben etwas kurz und natürlich nicht klar was man machen muss, wenn man die beiden Frameworks gar nicht kennt. Werde ich mal überarbeiten, danke auf jeden Fall für das Feedback.
Gruß,
Michael
Comment
22 Re: Problem loading config values
somehow you missed step 3 in the installation instructions in this article as well as in the install.nfo file. Anyway, I aggree that the plugin could load some default configurations when there is no config file found.
There should be an error message, but as you correctly found out, the condition is crap, to read the showHelp config doesn't make any sense when there are no configs loaded. ;-)
Thx for that!
Question
23 vertical to horizontal layout
Comment
24 Re: vertical to horizontal layout
div.fallback label {
display: inline;
.
.
}
div.rating label {
display: inline;
}
Bug
25 Problems with custom routes
there should be an error in the redirect with custom routes (named controllers) after saving the rate.
Example:
Router::connect(
'/government',
array('controller' => 'products', 'action' => 'display', 5)
);
There return an error view by calling the XMLHttpRequest.responseText…
Comment
26 bugfix in js
url: ratingSettings[element]['config']['appRoot'] + '/rating/ratings/save/' + data[0] + '/' + data[3] + '/' + value + '?' + Math.floor(Math.random() * 999999),
Comment
27 Great work! ...
first of all i wish to thank Michael for this awesome piece of code.
Got it, had to use the element like this:
<?php
echo $this->element('rating', array('plugin' => 'rating',
'model' => 'Provider',
'id' => $provider['Provider']['id']));
?>
Just can repeat myself: Great work! Thanks a lot.
Comment
28 If you have custom catchall routes
Router::connect('/rating/ratings/:action/*', array('controller' => 'ratings','plugin'=>'rating'));Comment
29 "field ambiguous" error thrown for "view" method
Controller Class:
<?php// $values = $modelInstance->find(array($modelInstance->primaryKey => $id), //causes "field 'id' is ambiguous" warning
//
//fix:
//
$values = $modelInstance->find(array($modelInstance->name.".".$modelInstance->primaryKey => $id),
array(Configure::read('Rating.modelAverageField'),
Configure::read('Rating.modelVotesField')),
null,
-1);
?>
Comment
30 Not Working
help me to crack
Thanks in Advances..
Comment
31 RE: Not Working
Question
32 Use Auth component
$config['Rating.sessionUserId'] = 'Auth.User.id';
does not work for me
Every time a see: Warning: No valid user id was found at "Auth.User.id" in the session
Comment
33 Re: Use Auth component
If that warning goes away once you log in, then just turn off the help text in the plugin's config file.
Comment
34 stars disappear
When I disable javascript I see the star ratings, status text, radio buttons, vote button, and mouseover messages. Also, when javascript is disabled I can vote and it works fine, adding my vote to the database. So my guess is something is happening in the rating_query.js that fouls up the display.
Any thoughts on how to fix this would be greatly appreciated.
Comment
35 Re: stars disappear