Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

colinlongworth's avatar

Eloquent Scope for average result?

I'm following along with Eloquent Performance Patterns, which is brilliant, and decided to tackle something of my own.

At a high level, A Student can have many Enrollments. When paginating the enrollments, I'd like to include a scope that contains the student's average percentage completion of all their enrollments (Thus on a front end table there could be a comparison between that course and the student's overall progress)

Additionally, I'd like to be able to pass a variable to limit this calculation to a given subset of courses.

class Enrollment extends Model
{
    protected $fillable = [
        'student_id',
        'course_id',
        'percentage_completed', //A float, where 1.0 would be 100%
    ];

    public function scopeWithAvgPercentageCompleted($query, $course_ids)
    {
        $query->addSelect(['avg_percentage_completed' => function (Builder $builder) use ($course_ids) {
            $builder
                ->from('enrollments')
                ->selectRaw('ROUND(avg(percentage_completed) * 100, 2) as avg_percentage_completed')
                ->whereIn('course_id', $course_ids);
        }]);
    }

}

However, when I run the query, I'd get very weird numbers for avg_percentage_completed e.g. Values greater than zero, even though the student has progressed through nothing.

//Show the enrollments for students with ID 1, 4 and 6 in the Course IDs for 123, 456, 789
Enrollment::whereIn('student_id', [1, 4, 6])->withAvgPercentageCompleted([123, 456, 789])->paginate()

I assume I'm looking at this the wrong way, but any pointers would be appreciated.

0 likes
2 replies
colinlongworth's avatar

@dev-mike My hesitance to use a mutator is that it will create another database call, or would it? Can you lazy load a mutator?

Edit: Another issue with a mutator is the inability to pass an array of course ids to filter. So it's find for the average of all enrollments, but not for a specific subset.

Please or to participate in this conversation.