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

schroedermatthias's avatar

How to build a worklog table by month?

Hi folks,

I have got worklogs, each of them are assigned to a project (1:1). A worklog consists only of a date (when the work was done) and the relation to the project.

How can I create a table, that shows all worklogs summed up for all projects of one year?

The table should be composed in a way, that the first column contains all projects (that have worklogs for this year), all further columns contain the months. The last column should sum up all months of the project. The last row should sum up all projects of one month. The cell right at the bottom should be the sum of all.

Example:

|Project|January|February|March|April|...|December| Sum |
|---|---|---|---|---|---|---|---|
|Project 1| 22 | 0 | 0 | 13.5 | ... | 38 | 73,5 |
|Project 2| 14 | 7 | 3 | 24 | ... | 0 | 48 |
|Project 3| 11 | 10 | 0 | 0 | ... | 9 | 30 |
| Sum | 47 | 17 | 3 | 27.5 | ... | 47 | 198,5 |

How would you query? Ist it possible only with querying or do I need some Model/Controller logic that build a suitable structure for the template? Or would you even calculate in the template? In general I think I could solve this myself, but I want a slim and clean approach.

I am looking forward to your ideas and really appreciate your help.

Matthias

0 likes
4 replies
xmarks's avatar

How do you differentiate years here?

Are worklogs from January 2016 and worklogs from January 2017 both summed-up in the same Table-cell?

xmarks's avatar
xmarks
Best Answer
Level 8

It's gonna be a bit hard for me, without making tests, to provide a working answer right away, but I'll try my best.

First of all, I think you should make the transformations in your View in this case. Some of the logic in the Controller and Model. And just query the data from the Database.

projects Table

| fields |
| ------ |
| id |
| name |
| created_at |
| updated_at |

worklogs Table

| fields |
| ------ |
| id |
| project_id |
| log |
| created_at |
| updated_at |

Query

Based on the response here, I think this should work:

$year = $request->year;

$workLogs = Project::leftJoin('worklogs', 'projects.id', '=', 'worklongs.project_id')
    ->whereRaw("(YEAR(worklongs.created_at) =  ?)", [$year])
    ->select(
        'projects.id as id',
        'projects.name as name',
        
        DB::raw('MONTHNAME(worklogs.created_at) as month'),
        DB::raw('COUNT(DISTINCT worklogs.id) as month_total')
    )->orderBy('worklogs.id', 'month')
    ->orderBy(DB::raw('FIELD(MONTH,"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December")'))
    ->groupBy('worklogs.id', 'month')->get();

This query will need to be tweaked a little so it shows something when the month_count is 0. This should get data like below:

| id | name | month | month_total |
| ------ | ------ | ------ | ------ |
| 1 | project_1 | January | 5 |
| 1 | project_1 | February | 10 |
| 2 | project_2 | January | 15 |
| 3 | project_3 | December | 4 |
Next, Group them by ID to separate Projects
$projects = $workLogs->groupBy('id');

This will give a collection, groupedBy Projects.

View

<table class="table table-striped table-responsive">
    <thead>
        <tr>
            <th>Project</th>
            <th>January</th>
            <th>February</th>
            <th>March</th>
            <th>April</th>
            <th>May</th>
            <th>June</th>
            <th>July</th>
            <th>August</th>
            <th>September</th>
            <th>October</th>
            <th>November</th>
            <th>December</th>
            <th>Total</th>
        </tr>
    </thead>
    
    <tbody>
    <!-- Treat each $projects Collection as 1-Project -->
        @foreach($projects as $project)
            <tr>
                <th scope="row">{{ $project->first()->name }}</th>
                <!-- Treat the data inside each project as 1-separate Worklog -->
                <!-- Assuming the Query is fixed above - Each month should be iterated -->
                <?php $totalCount = 0; ?>
                @foreach($project->all() as $worklog)
                    <td>{{ $worklog->month_total }}</td>
                    <?php $totalCount += $worklog->month_total; ?>
                @endforeach
                <td>{{ $totalCount }}</td>
            </tr>
        @endforeach
    </tbody>
</table>

Edit: Can't find anything to set a default month_total Count to be 0, if there are no worklogs at all for a given month.

Only solution would be to create a separate Table months, which would store all 12-month names. Then add a join to modify the query. Or join a subQuery as suggested Here

Please or to participate in this conversation.