Skip to content

Commit 4db28aa

Browse files
Fix groupBy to resolve dot-notation keys into SQL joins
Dot-notation column keys (e.g. contract.department.name) are Eloquent relationship paths, not SQL columns. The groupBy/aggregate selectRaw was using them directly, causing "column not found" errors. - Add resolveColumnWithJoins() to walk the relationship chain and add proper joins for BelongsTo/HasOne/HasMany relationships - Replace normal columns with group_value + aggregate columns when grouping is active - Update displayColumns on groupBy/aggregate property changes Closes #109 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 85599c1 commit 4db28aa

2 files changed

Lines changed: 71 additions & 8 deletions

File tree

src/ReportBuilder.php

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
use ACTTraining\QueryBuilder\Support\Concerns\WithReportBuilder;
88
use Illuminate\Contracts\View\Factory;
99
use Illuminate\Contracts\View\View;
10+
use Illuminate\Database\Eloquent\Builder;
11+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
12+
use Illuminate\Database\Eloquent\Relations\HasMany;
13+
use Illuminate\Database\Eloquent\Relations\HasOne;
1014
use Livewire\Attributes\Computed;
1115

1216
abstract class ReportBuilder extends QueryBuilder
@@ -28,12 +32,66 @@ public function rowsQuery()
2832
return $query;
2933
}
3034

31-
$query->selectRaw("{$this->groupBy}, {$this->aggregateFunction}({$this->aggregateColumn}) as aggregate")
32-
->groupBy($this->groupBy);
35+
$groupByColumn = $this->resolveColumnWithJoins($query, $this->groupBy);
36+
$baseTable = $query->getModel()->getTable();
37+
$aggregateColumn = str_contains($this->aggregateColumn, '.')
38+
? $this->resolveColumnWithJoins($query, $this->aggregateColumn)
39+
: "{$baseTable}.{$this->aggregateColumn}";
40+
41+
$query->selectRaw("{$groupByColumn} as group_value, {$this->aggregateFunction}({$aggregateColumn}) as aggregate")
42+
->groupBy($groupByColumn);
3343

3444
return $query;
3545
}
3646

47+
protected function resolveColumnWithJoins(Builder $query, string $key): string
48+
{
49+
if (! str_contains($key, '.')) {
50+
return $query->getModel()->getTable().'.'.$key;
51+
}
52+
53+
$parts = explode('.', $key);
54+
$columnName = array_pop($parts);
55+
$currentModel = $query->getModel();
56+
$joined = [];
57+
58+
foreach ($parts as $relationName) {
59+
if (! method_exists($currentModel, $relationName)) {
60+
return $key;
61+
}
62+
63+
$relation = $currentModel->{$relationName}();
64+
$relatedTable = $relation->getRelated()->getTable();
65+
66+
if (in_array($relatedTable, $joined, true)) {
67+
$currentModel = $relation->getRelated();
68+
69+
continue;
70+
}
71+
72+
if ($relation instanceof BelongsTo) {
73+
$query->join(
74+
$relatedTable,
75+
$currentModel->getTable().'.'.$relation->getForeignKeyName(),
76+
'=',
77+
$relatedTable.'.'.$relation->getOwnerKeyName()
78+
);
79+
} elseif ($relation instanceof HasOne || $relation instanceof HasMany) {
80+
$query->join(
81+
$relatedTable,
82+
$currentModel->getTable().'.'.$currentModel->getKeyName(),
83+
'=',
84+
$relatedTable.'.'.$relation->getForeignKeyName()
85+
);
86+
}
87+
88+
$joined[] = $relatedTable;
89+
$currentModel = $relation->getRelated();
90+
}
91+
92+
return $currentModel->getTable().'.'.$columnName;
93+
}
94+
3795
public function render(): Factory|View
3896
{
3997
return view('query-builder::report-table');

src/Support/Concerns/WithReportBuilder.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,19 @@ public function updatedSelectedColumns(): void
5757
public function updatedGroupBy(): void
5858
{
5959
$this->resetPage();
60-
$this->dispatch('refreshTable')->self();
60+
$this->displayColumns = $this->resolveColumns()->pluck('key')->toArray();
6161
}
6262

6363
public function updatedAggregateFunction(): void
6464
{
6565
$this->resetPage();
66-
$this->dispatch('refreshTable')->self();
66+
$this->displayColumns = $this->resolveColumns()->pluck('key')->toArray();
6767
}
6868

6969
public function updatedAggregateColumn(): void
7070
{
7171
$this->resetPage();
72-
$this->dispatch('refreshTable')->self();
72+
$this->displayColumns = $this->resolveColumns()->pluck('key')->toArray();
7373
}
7474

7575
public function hasGroupBy(): bool
@@ -176,9 +176,14 @@ public function buildColumns(): array
176176
}
177177

178178
if ($this->hasGroupBy()) {
179-
$columns[] = Column::make('Aggregate', 'aggregate')
180-
->justify('right')
181-
->sortable();
179+
$groupByLabel = $this->findElementByKey($this->availableColumns(), $this->groupBy)['label'] ?? $this->groupBy;
180+
181+
return [
182+
Column::make($groupByLabel, 'group_value')->sortable(),
183+
Column::make("{$this->aggregateFunction}({$this->aggregateColumn})", 'aggregate')
184+
->justify('right')
185+
->sortable(),
186+
];
182187
}
183188

184189
return $columns;

0 commit comments

Comments
 (0)