Creating a star rating percentage in Laravel

10,261

Okay, so you have a star based rating system out of 5 stars. I'm going to make a few assumptions/suggestions below.

I assume you have a ratings table that literally just stores one row per rating, if not, I'd recommend setting one up.

<?php
class Rating extends \Eloquent
{

    protected $fillable = ['profile_id', 'user_id', 'rating'];

    public function user()
    {
        return $this->belongsTo('User', 'user_id');
    }

    public function profile()
    {
        return $this->belongsTo('Profile', 'profile_id');
    }

}

This table will serve as a good way to keep track of ratings, plus it allows you to limit ratings if you add timestamps and some logic in your controller.

You've got several options here, and they are as follows.

  1. Have a column on the profile which contains the current rating, and is updated when a new rating is added.
  2. Have a summary table that contains the rating and is updated when a new rating is added.
  3. Dynamically work it out.

The way to work out the rating for option 1 would be the same as option 3, except you'd only run it once a new rating was added. Take the below pseudo code.

rating = score / ((total_ratings * 5) / 100)

In this example, score is the sum of all ratings and total_ratings is the count of ratings.

Rather than have the code in your controller, you could abstract it out to a Laravel mutator in the model, which for a dynamic value would look like the following:

public function getRating()
{
    return Profile::join('ratings', 'ratings.profile_id', '=', 'profile.id')
        ->where('profile.id', $this->attributes['id'])
        ->select(DB::raw('SUM(ratings.rating) / ((COUNT(ratings.* * 5) / 100) as rating'))->pluck('rating');
}

Now when accessing your profile/supplier model, you can just access the rating property like you would any other column.

If you wanted this to be stored, you'd use the same sort of method to work it out in the controller. Ultimately you're not limited to any particular method, and there are more than three that I mentioned, but those are the base ones.

Hope that helps.

Share:
10,261
Jono20201
Author by

Jono20201

Software engineer specialising in web-based apps, SaaS solutions and, cloud infrastructure. If you need more help than StackOverflow provides then I do provide a reasonably priced consultancy service. Contact: jono20201[at]gmail[dot]com

Updated on June 04, 2022

Comments

  • Jono20201
    Jono20201 over 1 year

    I am creating an application where buyers can rate their supplier against a project, they rate them on a 'out of five' basis (with stars) however on the supplier profile I want to display this data as a normal percentage. I have gotten this to work with this following code, but I wanted to know if there was a cleaner way to do this as It seems I would have to replicate this code 5 times (or create a function for it) but I was hoping Laravel may have a better way to do it.

    public function getSupplierProfile($group_id) {
        $group = Group::findOrFail($group_id);
    
        $data = new stdClass;
    
        $data->quality = new stdClass;
        $data->quality->query = SupplierRating::where('supplier_id', '=', $group->id);
        $data->quality->count = $data->quality->query->count();
        $data->quality->star_avg = $data->quality->query->avg('quality');
        $data->quality->avg = $data->quality->star_avg / ($data->quality->count * 6) * 100;
    
        dd($data->quality->avg); // debug
    
        return View::make('groups.view_profile', array('group' => $group));
    }
    
    • The Alpha
      The Alpha over 9 years
      You are passing $group to the view then why all the $data ?
    • Jono20201
      Jono20201 over 9 years
      Currently just working on how to best pass the actual percent data to the view so it can be easily displayed, I will move all the processing into the Group or SupplierRating model once I have it figured.
    • Arthur BALAGNE
      Arthur BALAGNE over 9 years
      For me, the easiest way to do that, is to create an attribute $data fill it with whatever you need in the __contruct function.
    • Jarek Tkaczyk
      Jarek Tkaczyk over 9 years
      Have you thought about scalability? How would you present these ratings for a list of suppliers? What you have now leads to querying ratings table for every supplier on the list. You'd better do the math on the DB side, only then setup accessor for that percentage rating.
    • Jono20201
      Jono20201 over 9 years
      @deczo At no point will these values be presented in a list with lots of suppliers, that's something our application explicitly doesn't want. If for some reason we do start listing them I would cache the values in the suppliers database table.