AJAX star rating plugin

By Michael Schneidt (schneimi)
In the days of social networks, you often want to give users the possibility to rate things and to show the average rating. The most common user interface implementation is a star rating system. This plugin offers you an easy, customizable way to enable your users to star rate any CakePHP model you want.

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 at
http://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'));

    Page 2: Configuration

    Comments 1081

    CakePHP Team Comments Author Comments
     

    Comment

    1 Super Cool!

    Many thanks for this super cool plugin! Great job well done!

    Thanks
    Posted Jul 8, 2009 by Sola Ajayi
     

    Comment

    2 Little Help with Plugin Models

    Hi, I have a little problem with rating plugin model content.

    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.
    Posted Jul 9, 2009 by Sola Ajayi
     

    Comment

    3 Little Help with Plugin Models

    Hi,

    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
    Posted Jul 9, 2009 by Michael Schneidt
     

    Comment

    4 Little Help with Plugin Models

    Hi,

    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.
    Posted Jul 10, 2009 by Sola Ajayi
     

    Comment

    5 Little Help with Plugin Models

    Hi,

    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.
    Posted Jul 10, 2009 by Michael Schneidt
     

    Comment

    6 Little Help with Plugin Models

    Thank you very much! It now works!
    Posted Jul 10, 2009 by Sola Ajayi
     

    Comment

    7 Error when rating...

    Hey, I am new to cake but managed to complete both tutorials (Blog & ACL). I also did install the rating plugin, which is displayed fine. But as soon as I want to rate the following error occurs:


    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!
    Posted Jul 22, 2009 by Robert
     

    Question

    8 Error when rating...

    Hey, I am new to cake but managed to complete both tutorials (Blog & ACL). I also did install the rating plugin, which is displayed fine. But as soon as I want to rate the following error occurs:


    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!
    Posted Jul 22, 2009 by Robert
     

    Question

    9 re: Error when rating...

    Hey, I am new to cake but managed to complete both tutorials (Blog & ACL). I also did install the rating plugin, which is displayed fine. But as soon as I want to rate the following error occurs:
    ....

    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('*');
            }
    Posted Jul 22, 2009 by Robert
     

    Comment

    10 Good Job!

    At a glance, seems to be a very nice plugin. Worked like a charm into my cake project. But there is some details that I would like to discuss.

    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.
    Posted Aug 5, 2009 by Yuri Teixeira
     

    Comment

    11 Re: Good Job!

    Thanks for your hint and solution, that makes sense and is no problem to change because it should work in each case. Only the code has to be moved inside the js-files because Prototype must be supported as well.

    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
    Posted Aug 6, 2009 by Michael Schneidt
     

    Comment

    12 Re: Re: Good Job!

    Sorry, forget about my last post, unfortunately that's not a solution because the window is only loaded once, meaning that there won't be further initialisations on the ajax updates that are important for an updated browser interface.

    I currently see no way how to handle that, if you find something working let me know.
    Posted Aug 10, 2009 by Michael Schneidt
     

    Comment

    13 Thanks!

    Thanks for this great plug in!!!

    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
    Posted Aug 12, 2009 by Nahuel
     

    Comment

    14 Images Missing

    I'm having a problem where my Images go missing and instead of pointing at

    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 ?
    Posted Oct 3, 2009 by Steve
     

    Comment

    15 Re: Images Missing

    The plugin uses the paths to the images that are set in the app/config/plugin.rating.php, default is "rating/img/star-full.png".

    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.
    Posted Oct 4, 2009 by Michael Schneidt
     

    Comment

    16 flash msg

    Wonderful plugin. Works perfectly.
    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?
    Posted Oct 16, 2009 by Julien Brinas
     

    Comment

    17 Re: flash msg

    Thx for your comment, I really forgot about this scenario.

    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
    Posted Oct 16, 2009 by Michael Schneidt
     

    Comment

    18 Re: Re: flash msg

    To solve the flash collisions you simply have to use a flash key, as shown in the following code.
    Works like a charm. Thanks!
    Posted Oct 17, 2009 by Julien Brinas
     

    Comment

    19 lot of thx

    very nice script ...
    very useful...
    Posted Oct 23, 2009 by satish
     

    Bug

    20 Problem loading config values - SOLVED

    This was the first plugin I've ever installed, so I expected some hiccups.

    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?
    Posted Oct 28, 2009 by Janice O'Toole
     

    Comment

    21 Re: protoaculous

    Hi Seven,

    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
    Posted Oct 28, 2009 by Michael Schneidt
     

    Comment

    22 Re: Problem loading config values

    Hi Janice

    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!
    Posted Oct 28, 2009 by Michael Schneidt
     

    Question

    23 vertical to horizontal layout

    Thanks for the plugin! I've got it working but just wondering why its layed out vertically? maybe this is related to my other css? I couldn't see a setting about this anywhere.
    Posted Nov 9, 2009 by Jason
     

    Comment

    24 Re: vertical to horizontal layout

    Someone else had this problem with using the cake.generic.css, where labels are declared as block elements. This is already fixed for the next version, so long you can just change/add the following parts in the rating\vendors\css\rating.css:

    div.fallback label {
      display: inline;
      .
      .
    }
    div.rating label {
      display: inline;
    }
    Posted Nov 10, 2009 by Michael Schneidt
     

    Bug

    25 Problems with custom routes

    Hi Michael,

    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…
    Posted Nov 11, 2009 by malte
     

    Comment

    26 bugfix in js

    it must be an absolute path in rating_jquery.js (line:132)… did you have a better solution? By exampe when the "appRoot" path is define by "/my_path/"…


    url: ratingSettings[element]['config']['appRoot'] + '/rating/ratings/save/' + data[0] + '/' + data[3] + '/' + value + '?' + Math.floor(Math.random() * 999999),
    Posted Nov 11, 2009 by malte
     

    Comment

    27 Great work! ...

    Hello Michael, hello Cakebakers,

    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.
    Posted Nov 20, 2009 by Daniel
     

    Comment

    28 If you have custom catchall routes

    If you are overriding the default catchall route (/:controller/:action), here is a route to get you to the controller:

    Router::connect('/rating/ratings/:action/*', array('controller' => 'ratings','plugin'=>'rating'));
    Posted Nov 25, 2009 by Sequoia McDowell
     

    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);
    ?>
    Posted Nov 25, 2009 by Sequoia McDowell
     

    Comment

    30 Not Working

    Hi i am trying to integrate this plugin in my project, i dont have any problem in the view , working fine. When i click on rating the ajax request is made to rating/ratings/save/. , having all the parameters in the url. but not updated in the database. after the request , the whole page is loading in that particular div. i dont know what is the page.

    help me to crack

    Thanks in Advances..
    Posted Nov 26, 2009 by Madhan
     

    Comment

    31 RE: Not Working

    Looks like another routing problem. Maybe what Sequoia wrote in comment 29 could help you.
    Posted Nov 27, 2009 by Michael Schneidt
     

    Question

    32 Use Auth component

    how i can integrate with cake 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
    Posted Dec 6, 2009 by Bruno Trazzini
     

    Comment

    33 Re: Use Auth component

    It works for me using Auth.User.id. If you aren't logged in you'll get that warning if you have it set to not allow guests, and you have the help text turned on in your settings.

    If that warning goes away once you log in, then just turn off the help text in the plugin's config file.

    Posted Jan 4, 2010 by Tony Maro
     

    Comment

    34 stars disappear

    I am using jquery 1.1.4 minified and have set this plugin up according to the directions. However, when my page loads I can see the stars for a split second then they disappear and the rating status text is all that is left.

    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.
    Posted Feb 5, 2010 by Foroct Fralion
     

    Comment

    35 Re: stars disappear

    Check the appRoot setting in your config file, it is used in js along with the image locations to change the source of the images. If the source is wrong, the images set in the view disappear on js initialization.
    Posted Feb 7, 2010 by Michael Schneidt