Question

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));
}
Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top