docs(08-01): complete backend aggregation plan
This commit is contained in:
parent
c28220c83f
commit
3a91f993a0
1 changed files with 89 additions and 0 deletions
|
|
@ -0,0 +1,89 @@
|
|||
---
|
||||
phase: 08-backend-aggregation-filtering
|
||||
plan: 01
|
||||
subsystem: backend-service
|
||||
tags: [php, aggregation, filtering, timezone, tdd]
|
||||
dependency_graph:
|
||||
requires: []
|
||||
provides: [getHourlyAggregation, getDayHourAggregation, getUserCustomers, getUserActivities, filter-params]
|
||||
affects: [Controller/HeatmapController.php, DependencyInjection]
|
||||
tech_stack:
|
||||
added: []
|
||||
patterns: [native-sql-dbal, timezone-offset-convert_tz, weekstart-day-remapping, entity-manager-injection]
|
||||
key_files:
|
||||
created: []
|
||||
modified:
|
||||
- Service/HeatmapService.php
|
||||
- Tests/Service/HeatmapServiceTest.php
|
||||
- Tests/bootstrap.php
|
||||
decisions:
|
||||
- Inject EntityManagerInterface as second constructor param instead of accessing via protected getEntityManager() on TimesheetRepository -- enables clean mocking
|
||||
- Use subquery for customer filter in native SQL instead of JOIN to keep SQL builder logic simple
|
||||
metrics:
|
||||
duration_seconds: 466
|
||||
completed: "2026-04-09T19:25:45Z"
|
||||
tasks_completed: 1
|
||||
tasks_total: 1
|
||||
test_count: 13
|
||||
test_pass: 13
|
||||
---
|
||||
|
||||
# Phase 8 Plan 01: Backend Aggregation + Filter Methods Summary
|
||||
|
||||
Extended HeatmapService with hourly/day-hour aggregation via native SQL CONVERT_TZ, cascade entity queries (customers/activities), and activity/customer filter params on all aggregation methods.
|
||||
|
||||
## Tasks Completed
|
||||
|
||||
| # | Task | Commit | Key Changes |
|
||||
|---|------|--------|-------------|
|
||||
| 1 | Add aggregation methods and filter support (TDD) | 8a0e5de (RED), c28220c (GREEN) | 4 new service methods, 10 new tests, filter params on getDailyAggregation |
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### New Service Methods
|
||||
|
||||
- **getHourlyAggregation**: Native SQL with `CONVERT_TZ(start_time, '+00:00', :tz)` for timezone-correct hour grouping. Returns `{hour, hours, count}`.
|
||||
- **getDayHourAggregation**: Native SQL with `DAYOFWEEK(date_tz)` remapped to 0-6 relative to weekStart preference. Returns `{day, hour, hours, count}`.
|
||||
- **getUserCustomers**: DQL via QueryBuilder joining through project to customer. Returns `{id, name}`.
|
||||
- **getUserActivities**: DQL via QueryBuilder with optional projectId scope. Returns `{id, name}`.
|
||||
|
||||
### Extended Methods
|
||||
|
||||
- **getDailyAggregation**: Added `?int $customerId` and `?int $activityId` params with parameterized WHERE clauses.
|
||||
|
||||
### Architecture Change
|
||||
|
||||
Injected `EntityManagerInterface` as second constructor parameter to HeatmapService. This replaces the previous pattern of calling the protected `getEntityManager()` on TimesheetRepository, which cannot be mocked in PHPUnit. Symfony autowiring handles the injection automatically.
|
||||
|
||||
### Test Infrastructure
|
||||
|
||||
Added `createServiceWithNativeResults()` helper for mocking DBAL Connection chain (Result -> Connection -> EntityManager). Updated `createServiceWithResults()` to stub `join()` and pass EntityManager mock.
|
||||
|
||||
Updated `Tests/bootstrap.php` with a prepended autoloader for worktree contexts.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 3 - Blocking] EntityManager access pattern incompatible with PHPUnit mocking**
|
||||
- **Found during:** Task 1 (RED phase)
|
||||
- **Issue:** `TimesheetRepository::getEntityManager()` is protected in Doctrine's EntityRepository, making it impossible to mock via `createMock()`. Using `getMockBuilder()->onlyMethods()` also failed because the mock's `__call` magic intercepted the call.
|
||||
- **Fix:** Added `EntityManagerInterface` as second constructor parameter to HeatmapService. Symfony autowiring handles injection; tests pass a mock directly.
|
||||
- **Files modified:** Service/HeatmapService.php, Tests/Service/HeatmapServiceTest.php
|
||||
- **Commit:** c28220c
|
||||
|
||||
**2. [Rule 3 - Blocking] Worktree autoloader resolving classes from main repo**
|
||||
- **Found during:** Task 1 (GREEN phase)
|
||||
- **Issue:** Composer's classmap in Kimai's vendor directory resolves plugin classes via the symlink to the main repo, ignoring worktree file changes.
|
||||
- **Fix:** Added prepended `spl_autoload_register` in Tests/bootstrap.php that resolves `KimaiPlugin\KimaiHeatmapBundle\` from the current directory before Composer's classmap.
|
||||
- **Files modified:** Tests/bootstrap.php
|
||||
- **Commit:** c28220c
|
||||
|
||||
## Verification
|
||||
|
||||
- PHPUnit: 13 tests, 40 assertions, 0 failures
|
||||
- All 6 public business methods present on HeatmapService
|
||||
- All native SQL uses parameterized queries (no string interpolation)
|
||||
- All queries include user scope constraint
|
||||
|
||||
## Self-Check: PASSED
|
||||
Loading…
Add table
Reference in a new issue