laravellaravel-bladelaravel-query-builderlaravel-controller

how to calculate % progress based on two tables with where condition


What is the best way to calculate the % progress of the activity based on the completed activity task points and total activity task points at the same time get the total number of activity task

I have two tables

activity and task

Activity table

id, name, title,

Task table

id, name, status('In progress','Completed), activity_id, points

For Example

Activity table has this data

id name title
1 1.2.3 Conduct Animation Settion
2 1.3.2 Adocacy session

Task table has this data

id name status activity_id points
1 task 1 Completed 1 20
2 task 2 In Progress 1 10
3 task 3 In Progress 2 5
4 task 4 Completed 1 8
5 task 5 Completed 2 9

Expected output

where % progress is calculated from total points of completed activity task and the total points of an activity task

id name title task total_task_points total_completed_task_points %progress
1 1.2.3 Conduct Animation Settion 3 38 28 (total_completed_task_points / total_task_points)x100 = 73.68%
2 1.3.2 Adocacy session 2 14 9 (total_completed_task_points / total_task_points)x100 = 64.28%

What I have tried

On Activity Controller

   public function index()
    {
        try {
            $activities = Activity::with('task')->orderBy('name', 'asc')->get();
            return view('activities.index',compact('activities');
        }
        catch (\Exception $e) {
            return $this->error('Page Not Found');
        }
    }

On view File

percentage() is a helper for % calculation

<table>
<thead>
   <th>id</th>
   <th>Name</th>
   <th>title</th>
   <th>Task</th>
   <th>Points</th>
   <th>Progress</th>
</thead>
<tbody>
    @foreach ($activities as $activity)
       <tr>
          <td class="text-center">{{ $loop->iteration }}</td>
          <td class="text-start">{!! $activity->name !!} </td>
          <td class="text-start">{!! $activity->title !!} </td>
          <td  class="text-center">{!! $activity->task->count() !!}</td>
          <td  class="text-center">{!! $activity->task->sum('points') !!}</td>
          <td  class="text-center">
            <div class="progress">
               <div class="progress-bar bg-success" role="progressbar" 
                    style="width: {{percentage($activity->task->where('status','completed')->sum('points'),$activity->task->sum('points'))}}%"
                    aria-valuenow="{{percentage($activity->task->where('status','Completed')->sum('points'),$activity->task->sum('points'))}}"
                    aria-valuemin="0"
                    aria-valuemax="100">{{percentage($activity->task->where('status','Completed')->sum('points'),$activity->task->sum('points'))}}
                </div>
            </div>
          </td>
      </tr>
   @endforeach
</tbody>

As you can see on the view file is where all the calculations and filtering are done which is not a good practice. I need help


Solution

  • Rather than doing logic inside blade view.

    You can move your logic inside Model

    at your Activity Model

    class Activity extends Model
    

    you can add the following code.

    protected $appends = ['progress'];
    
    public function getProgressAttribute(){
        return round(($this->completedPoints() / $this->totalPoints()) * 100, 2);
    }
    
    public function totalPoints(){
        return $this->task->sum('points');
    }
    
    public function completedPoints(){
        return $this->task->where("status", "Completed")->sum('points');
    }
    

    Then inside your blade view you can access to $activity->progress