Schneimi’s Dev Weblog


Star rating plugin for CakePHP

Posted in Ajax, CakePHP by schneimi on the May 25, 2009
Tags: , , , , , ,

In the google groups I recently noticed the lack of a star rating helper/plugin for CakePHP.

As I just implemented an AJAX rating system for my current project and always wanted to learn the CakePHP plugin system, I thought this was a good opportunity to make my code into a plugin and contribute to the community.

My rating system supports multiple users and multiple models, so you can let people rate any model you want.

I tried to keep it simple and flexible, but I am sure there is still much room for improvements. So let me know what you think about it, any suggestions and comments are welcome.

Here is the plugin v2.2 to download.

This article can also be found in the Bakery now.

Features

  • Multi user, multi model rating
  • Just one element to place 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 javascript framework
  • User id stored in session for secure rating

Demonstration
A demo can be tested and downloaded at
http://ratingdemo.schneimi.spacequadrat.de/

    Installation and Use

    • Make sure you meet the requirements above. Make sure you meet the requirements above. For the download and integration of a javascript framework, please visit the Prototype or jQuery website.
      • Extract the plugin, including the subfolder ‘rating’, to your app plugins folder ‘app/plugins‘.
        • 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.
          • Apply the ‘install.sql’ to your database to create the ratings table.
            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`)
            );
            
          • Load the plugin javascript and css files in your layout file. Replace [your_framework] with prototype_min or jquery_min depending on the framework you use.
              <?php echo $javascript->link('/rating/js/[your_framework]'); ?>
              <?php echo $html->css('/rating/css/rating'); ?>
              
          • For full model integration in your app, apply the following relation to your models. (replace [name_of_your_model])
              var $hasMany = array('Rating' =>
                                   array('className'   => 'Rating',
                                         'foreignKey'  => 'model_id',
                                         'conditions' => array('model' => '[name_of_your_model]'),
                                         'dependent'   => true,
                                         'exclusive'   => true
                                   )
                             );  
          • 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] and [Rating.modelAverageField]).
            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’.

          • You can change the styles of the rating element in the css file ‘rating/vendors/css/rating.css’.
            • 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

                echo $this->element('rating', array('plugin' => 'rating',
                                                    'model' => '[name_of_your_model]',
                                                    'id' => [id_of_your_model]));
               

              More ratings 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.

                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.

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

            41 Responses to 'Star rating plugin for CakePHP'

            Subscribe to comments with RSS or TrackBack to 'Star rating plugin for CakePHP'.

            1. Ander said,

              Very cool!
              Thank you :-)

            2. schneimi said,

              You can now choose between jQuery and prototype for ajax in the configs.

            3. bryan said,

              hey man

              looks great… having a problem thoug

              have followed instructions step by step but where do you store the rating_app_controller + model.php?

              i get

              Fatal error: Class ‘RatingAppModel’ not found in /var/www/localhost/htdocs/app/models/rating.php on line 2

              thanks!

              • schneimi said,

                Hi bryan,

                the controller as well as the model should rest inside the plugin structure, you don’t need and have to copy any files to your app.

                If all is at it’s place, I can’t tell why you get this error. Do you use the latest cake? Maybe there is a problem with a older CakePHP version.

                Btw I just released v1.4 with some other improvements.

            4. monir said,

              Thank you for the plugin it works great

            5. schneimi said,

              v1.5
              - configurable mouseover messages
              - cakephp conform config handling
              - cakephp conform model loading
              - changed sql createdate and changedate
              - reworked js events
              - separated framework js code
              - minified js files
              - tested on IE, FF, safari, chrome, opera

              Enjoy!

            6. peter said,

              Not working for me! :(

              • schneimi said,

                Hi Peter,

                could you be a little more specific what exactly doesn’t work? Do you at least see the rating element in your views? If not, are there shown any errors in debug mode?

                • peter said,

                  I am new with ajax, maybe thats the problem, i do see the stars but nothing happens when i place my mouse over them, neither when i click them, i do have the user model and the user id session.

                  PD: Thans a lot for quick your response. :)

                • schneimi said,

                  Thx for the info, it looks like you didn’t load jQuery or Prototype properly in your layout, that’s one of the requirements.

                  If you don’t know how to load it, or get other problems, please write me a mail for further help.

                • peter said,

                  I do have jquery working with other javascripts, but thanks for the help i will check it more carefully.
                  PD: I didt not found a contact or mail

                • schneimi said,

                  Then it must be the plugin javascript that is not properly loaded, maybe there is a problem with the ‘/rating/js/rating_jquery’ path. Let me know if you can work it out.

                  Thx for the missing contact info, I just added a page. ;-)

                • peter said,

                  I managed to make the javascript run, I bealive it was because i had both the prototype and the jquery javascript declared…

                  But now i dont see the stars (I doo see the really good , bad, etc.. messages)… when i click the place where the star shoul be i see this error :

                  “Object not found!
                  The requested URL was not found on this server. The link on the referring page seems to be wrong or outdated. Please inform the author of that page about the error.

                  If you think this is a server error, please contact the webmaster.

                  Error 404
                  localhost
                  06/15/09 16:37:57
                  Apache/2.2.11 (Win32) DAV/2 mod_ssl/2.2.11 OpenSSL/0.9.8i mod_autoindex_color PHP/5.2.8 ”

                  What am I doing wrong?
                  Thanks again for your time

                • schneimi said,

                  Ok yes, both frameworks bite each other.

                  About the images, I need to know the full link of the stars not showing. Settings concerning this are appRoot and the image locations (starFullImageLocation,…), maybe you can play around with them and work it out.

                  If not, please write me the details to michael.schneidt@arcor.de, thx.

                • Anders said,

                  I am having the same problem. I am using Jquery, but have not declared both prototype and Jq. I am using other JQ scripts on the site..

                  I can see empty stars, but nothing happens when i click on them / mouseover. :S

                  Help is appreciated :)

            7. peter said,

              Any working examples?

              • schneimi said,

                No site online yet, but I will see if I can set up a demo somewhere.

            8. peter said,

              works great!!!
              Thanks a lot!!
              :)
              Congrats for you job, is very flexible and easy to use

            9. peter said,

              Could it be possible to add more than one voting for the same element?… for example at the begining and at the end of a new…

              • schneimi said,

                Good idea, thx! I think about giving the elements an additional user defined name to separate them.

              • schneimi said,

                v1.6 supports this now, enjoy.

            10. peter said,

              How can I allow unlogued users to vote? can the User id be stored in a cookie instead of a session???

              • schneimi said,

                Both points are not possible because of security issues. Anonymous ratings can be easily manipulated and cookies are client side, so you could edit them and become any user you want.

            11. schneimi said,

              v1.7
              - individual configuration of single elements
              - fixed stars update on user id not found
              - helper class for the view
              - changed appRoot format

            12. peter said,

              Is there a way to disable the voting and to show only the average voting? i wish to have previews of articles showing the average voting and to allow people to vote only when they see the complete view of the article…
              Any sugestion?

              • schneimi said,

                Hi,

                this is not possible by default, but you just have to do two quick changes to reach that behavior.

                1) add a new setting in app/config/plugin.rating.php

                $config['Rating.disable'] = true;
                

                2) use it in the condition in the rating/views/view.ctp

                  // decision to enable or disable the rating
                  $enable = $session->check(Configure::read('Rating.userIdInSession')) && !Configure::read('Rating.disable')
                              && (Configure::read('Rating.allowChange') || (!Configure::read('Rating.allowChange') && $data['userRating'] == 0));
                
                • peter said,

                  Thanks :)

            13. Francis said,

              I dont know hoy to make an user not logged in not to vote!
              If i dont write the User.id in the Session I get the error The User id was not found at “User.id” in the session

              And if I set the user.id session to an id that does not exist like 0 for example, then it takes it as valid and let it votes and saves it on the ratings with user_id 0

              Should I just left the session blank and place the show errors to false? how does the ‘Please login to rate’ message activates?

              Hope i explained myself rigth, i apologize for my english

              • schneimi said,

                Yes, you should set showErrors to false, this is just for installation to check if it works when the user is logged in. I will think about how to make this less confusing.

                The User.id 0 should not be used as a valid user, thx for that information I will fix it.

                The “Please login to rate” message appears on mouse over if showMouseOverMessages is set true and no User.id is found in session.

                • Francis said,

                  Thanks
                  Maybe if it shows as warning and not an error will be less confusing. ;)

            14. Robin said,

              Hi schneimi,
              Thanx for this great plugin. It works fine for me.. Everything looks great except it throws a Notice

              Notice (8): Undefined index: fallback [APP\plugins\rating\controllers\ratings_controller.php, line 189]

              • schneimi said,

                Hi Robin,

                the notice is catched in the latest version 2.2, I just forgot to update the blog, you can download it now.

            15. Robin said,

              Schneimi,

              In my application, the rating is displayed vertically. Should I change any configuration to display it vertically ?

            16. Robin said,

              “In my application, the rating is displayed vertically.”
              -> This problem occurred because of the default style of Cake PHP.

              I commented ‘display: block; ‘ for label class in cake.generic.css and its working fine now.
              label {
              /*display: block; */
              }

              • schneimi said,

                Thx for showing the css problem, with two small changes on the plugin the problem can also be solved.

                rating\views\helpers\rating.php: line 58

                $output .= $this->Form->label($model.'.rating',
                                              $htmlImage,
                                              array('for' => $model.'Rating'.ucfirst($options['name']).$id.$i,
                                                    'class' => 'rating'));
                

                rating\vendors\css\rating.css

                div.rating label {
                  display: inline;
                }
                
            17. schneimi said,

              If jQuery works for your other scripts then the plugin javascript is most likely not loaded.

              How does your layout look like? You need the link(‘/rating/js/rating_jquery_min’); ?> in it, the first slash in the path is necessary.

            18. Anders said,

              Yep, thats how i done it… i can look at the JS when i check the source code… :S

              Do i have to change to Jquery manually somewhere in the plugin?

            19. Anders said,

              Update:

              I used firebug and found out that this is ht eline causing the error:
              $(‘#’ + element + ‘_’ + i).bind(‘mouseenter’, {i: i}, function(e) {

              2 errors:
              $ is not a function
              and doc.body is undefined

              any thoughts?

            20. Anders said,

              Sorry for so many posts. Fixed it with “var $j = jQuery.noConflict();” in the beginning of the js file.

              Hope this helps somebody else :)

            21. schneimi said,

              Great you found a solution, could you also figure out the script(s) causing the conflict?


            Leave a Reply