Allow to customize statistic queries (#5827)

* use TimesheetStatisticsQUery for all repository calls
* send event to customize statistics query
This commit is contained in:
Kevin Papst
2026-02-24 13:27:48 +01:00
committed by GitHub
parent d9ef6dfcad
commit 77afa207e0
6 changed files with 124 additions and 10 deletions

View File

@@ -16,6 +16,7 @@ use App\Model\DateStatisticInterface;
use App\Model\Statistic\StatisticDate;
use App\Repository\ActivityRepository;
use App\Repository\ProjectRepository;
use App\Repository\Query\TimesheetStatisticQuery;
use App\Timesheet\TimesheetStatisticService;
use DateTimeInterface;
@@ -37,7 +38,7 @@ abstract class AbstractUserReportController extends AbstractController
protected function getStatisticDataRaw(DateTimeInterface $begin, DateTimeInterface $end, User $user): array
{
return $this->statisticService->getDailyStatisticsGrouped($begin, $end, [$user]);
return $this->statisticService->getDailyStatisticsGrouped(new TimesheetStatisticQuery($begin, $end, [$user]));
}
protected function createStatisticModel(DateTimeInterface $begin, DateTimeInterface $end, User $user): DateStatisticInterface

View File

@@ -17,6 +17,7 @@ use App\Model\DateStatisticInterface;
use App\Model\MonthlyStatistic;
use App\Reporting\YearByUser\YearByUser;
use App\Reporting\YearByUser\YearByUserForm;
use App\Repository\Query\TimesheetStatisticQuery;
use DateTime;
use DateTimeInterface;
use PhpOffice\PhpSpreadsheet\Reader\Html;
@@ -125,7 +126,7 @@ final class UserYearController extends AbstractUserReportController
protected function getStatisticDataRaw(DateTimeInterface $begin, DateTimeInterface $end, User $user): array
{
return $this->statisticService->getMonthlyStatisticsGrouped($begin, $end, [$user]);
return $this->statisticService->getMonthlyStatisticsGrouped(new TimesheetStatisticQuery($begin, $end, [$user]));
}
protected function createStatisticModel(DateTimeInterface $begin, DateTimeInterface $end, User $user): DateStatisticInterface

View File

@@ -0,0 +1,25 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Event;
use Doctrine\ORM\QueryBuilder;
use Symfony\Contracts\EventDispatcher\Event;
final class TimesheetStatisticsQueryEvent extends Event
{
public function __construct(private readonly QueryBuilder $queryBuilder)
{
}
public function getQueryBuilder(): QueryBuilder
{
return $this->queryBuilder;
}
}

View File

@@ -10,15 +10,19 @@
namespace App\Timesheet;
use App\Entity\User;
use App\Event\TimesheetStatisticsQueryEvent;
use App\Model\DailyStatistic;
use App\Model\MonthlyStatistic;
use App\Repository\Query\TimesheetStatisticQuery;
use App\Repository\TimesheetRepository;
use DateTimeInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
final class TimesheetStatisticService
{
public function __construct(private readonly TimesheetRepository $repository)
public function __construct(
private readonly TimesheetRepository $repository,
private readonly EventDispatcherInterface $eventDispatcher
)
{
}
@@ -72,6 +76,8 @@ final class TimesheetStatisticService
;
}
$this->eventDispatcher->dispatch(new TimesheetStatisticsQueryEvent($qb));
$results = $qb->getQuery()->getResult();
foreach ($results as $row) {
@@ -96,13 +102,14 @@ final class TimesheetStatisticService
/**
* @internal only for core development
* @param DateTimeInterface $begin
* @param DateTimeInterface $end
* @param User[] $users
* @return array
*/
public function getDailyStatisticsGrouped(DateTimeInterface $begin, DateTimeInterface $end, array $users): array
public function getDailyStatisticsGrouped(TimesheetStatisticQuery $query): array
{
$begin = $query->getBegin();
$end = $query->getEnd();
$users = $query->getUsers();
$stats = [];
$usersById = [];
@@ -137,6 +144,8 @@ final class TimesheetStatisticService
->addGroupBy('billable')
;
$this->eventDispatcher->dispatch(new TimesheetStatisticsQueryEvent($qb));
$results = $qb->getQuery()->getResult();
foreach ($results as $row) {
@@ -173,11 +182,14 @@ final class TimesheetStatisticService
/**
* @internal only for core development
* @param User[] $users
* @return array
*/
public function getMonthlyStatisticsGrouped(DateTimeInterface $begin, DateTimeInterface $end, array $users): array
public function getMonthlyStatisticsGrouped(TimesheetStatisticQuery $query): array
{
$begin = $query->getBegin();
$end = $query->getEnd();
$users = $query->getUsers();
$stats = [];
$usersById = [];
@@ -214,6 +226,8 @@ final class TimesheetStatisticService
->addGroupBy('billable')
;
$this->eventDispatcher->dispatch(new TimesheetStatisticsQueryEvent($qb));
$results = $qb->getQuery()->getResult();
foreach ($results as $row) {
@@ -311,6 +325,8 @@ final class TimesheetStatisticService
;
}
$this->eventDispatcher->dispatch(new TimesheetStatisticsQueryEvent($qb));
$results = $qb->getQuery()->getResult();
foreach ($results as $row) {

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Event;
use App\Event\TimesheetStatisticsQueryEvent;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\QueryBuilder;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
#[CoversClass(TimesheetStatisticsQueryEvent::class)]
class TimesheetStatisticsQueryEventTest extends TestCase
{
public function testGetter(): void
{
$qb = new QueryBuilder($this->createMock(EntityManager::class));
self::assertCount(0, $qb->getParameters());
$sut = new TimesheetStatisticsQueryEvent($qb);
$qb->setParameter('foo', 'bar');
self::assertSame($qb, $sut->getQueryBuilder());
self::assertCount(1, $sut->getQueryBuilder()->getParameters());
}
}

View File

@@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Event;
use App\Entity\User;
use App\Event\WorkingTimeQueryStatsEvent;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\QueryBuilder;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
#[CoversClass(WorkingTimeQueryStatsEvent::class)]
class WorkingTimeQueryStatsEventTest extends TestCase
{
public function testGetter(): void
{
$qb = new QueryBuilder($this->createMock(EntityManager::class));
self::assertCount(0, $qb->getParameters());
$user = new User();
$begin = new \DateTime('2004-02-13');
$end = new \DateTime('2099-12-31');
$sut = new WorkingTimeQueryStatsEvent($qb, $user, $begin, $end);
$qb->setParameter('foo', 'bar');
self::assertSame($qb, $sut->getQueryBuilder());
self::assertCount(1, $sut->getQueryBuilder()->getParameters());
self::assertSame($user, $sut->getUser());
self::assertSame($begin, $sut->getBegin());
self::assertSame($end, $sut->getEnd());
}
}