Вы в настоящее время запрашиваете все свои данные, группируете их в памяти, что на самом деле не совсем эффективно. Вместо этого вы должны как можно больше подтолкнуть к базе данных. Это также означает, что запрашиваются только соответствующие данные: category
, month
и sum(duration)
public function show(User $user)
{
$data = $user->times()
->whereYear('start_day', 2019)
->groupBy('category', \DB::raw('month(start_date)'))
->select([
'category',
\DB::raw('month(start_date) as month'),
\DB::raw('sum(duration) as duration'),
])
->orderBy(\DB::raw('month(start_date)'))
->get()
->mapToGroups(function ($item) {
$month = \DateTime::createFromFormat('!m', $item['month'])->format('M');
return [$month => [$item['category'] => $this->formatDuration($item['duration'])]];
})
->mapWithKeys(function ($item, $key) {
return [$key => $item->collapse()];
});
return view('table.show', compact('data'));
}
private function formatDuration($seconds)
{
$duration = '';
if ($seconds < 0) {
$duration = '-';
$seconds = abs($seconds);
}
$hours = floor($seconds / 3600);
$seconds -= $hours * 3600;
$minutes = floor($seconds / 60);
$seconds -= $minutes * 60;
return $duration . sprintf('%d:%02d:%02d', $hours, $minutes, $seconds);
}
На данный момент $data
содержит что-то следующего структуры:
[
'Jan' => [
'Category 1' => '1:20:40',
'Category 2' => '15:05:40',
'Category 4' => '0:00:50'
],
'Feb' => [
'Category 2' => '2:30:15',
'Category 3' => '4:45:30'
]
]
Мы можем использовать эти данные для генерации нашей таблицы в общем виде, используя индекс массива в качестве номера месяца.
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">Month</th>
<th scope="col">Overtime Hours</th>
<th scope="col">Compensation Hours</th>
<th scope="col">Vacation</th>
<th scope="col">Personal Hours</th>
<th scope="col">Sick Hours</th>
</tr>
</thead>
<tbody>
@foreach($data as $month => $row)
<tr>
<th scope="row">{{ $month }}</th>
<td>{{ $row['Overtime Hours'] ?? '-' }}</td>
<td>{{ $row['Compensation Hours'] ?? '-' }}</td>
<td>{{ $row['Vacation'] ?? '-' }}</td>
<td>{{ $row['Personal Hours'] ?? '-' }}</td>
<td>{{ $row['Sick Hours'] ?? '-' }}</td>
</tr>
@endforeach
</tbody>
</table>
Если вы хотите, чтобы каждый месяц отображался в таблице, даже еслиза месяц нет доступных данных, вы можете добавить отсутствующие индексы в коллекцию, используя array_fill(1, 12, [])
в качестве базы:
public function show(User $user)
{
$data = collect(array_fill(1, 12, []))
->replace(
$user->times()
->whereYear('start_day', 2019)
->groupBy('category', \DB::raw('month(start_date)'))
->select([
'category',
\DB::raw('month(start_date) as month'),
\DB::raw('sum(duration) as duration'),
])
->orderBy(\DB::raw('month(start_date)'))
->get()
->mapToGroups(function ($item) {
return [$item['month'] => [
$item['category'] => $this->formatDuration($item['duration'])
]];
})
->mapWithKeys(function ($item, $key) {
return [$key => $item->collapse()];
})
)
->mapToGroups(function ($item, $key) {
$month = \DateTime::createFromFormat('!m', $key)->format('M');
return [$month => $item];
});
return view('table.show', compact('data'));
}