From b5229b33b34d009942354f1ac3b69d8927c26631 Mon Sep 17 00:00:00 2001 From: Julian Vennen Date: Tue, 17 Mar 2026 20:33:47 +0100 Subject: [PATCH 1/9] Implement distinct selects --- src/Driver/Test/TestTable.php | 20 +++++++++++++++++++- src/Query/Generator/SQL.php | 4 ++++ src/Query/SelectQuery.php | 25 ++++++++++++++++++++++++- test/tests/SQLTest.php | 8 ++++++++ test/tests/TestDriverTest.php | 30 ++++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 2 deletions(-) diff --git a/src/Driver/Test/TestTable.php b/src/Driver/Test/TestTable.php index 9825aa6..83e21ba 100644 --- a/src/Driver/Test/TestTable.php +++ b/src/Driver/Test/TestTable.php @@ -72,7 +72,25 @@ public function query(Query $query): QueryResult if ($query instanceof SelectQuery) { /** @var class-string $modelClass */ $modelClass = $query->modelClassName; - $model = $modelClass::getModelFromData($entry->getDataForFields($query->getFields())); + $entryData = $entry->getDataForFields($query->getFields()); + $model = $modelClass::getModelFromData($entryData); + + if ($query->isDistinct()) { + foreach ($queryResult as $item) { + $same = true; + foreach ($entryData as $key => $value) { + if ($item->getField($key) !== $model->getField($key)) { + $same = false; + break; + } + } + + if ($same) { + continue 2; + } + } + } + $queryResult->add($model); } if ($query instanceof UpdateQuery) { diff --git a/src/Query/Generator/SQL.php b/src/Query/Generator/SQL.php index c37ca9a..dd6072c 100644 --- a/src/Query/Generator/SQL.php +++ b/src/Query/Generator/SQL.php @@ -73,6 +73,10 @@ public function generate(Query $query): string if ($query instanceof SelectQuery) { $queryString .= "SELECT"; + if ($query->isDistinct()) { + $queryString .= " DISTINCT"; + } + if ($query->getFields()) { $queryString .= " " . $this->generateFields($query); } else { diff --git a/src/Query/SelectQuery.php b/src/Query/SelectQuery.php index 16e649d..e8b192d 100644 --- a/src/Query/SelectQuery.php +++ b/src/Query/SelectQuery.php @@ -24,13 +24,16 @@ class SelectQuery extends Query * @param array|int|Limit|null $limit * @param array|GroupField[]|string[]|null $group * @param bool $saveResultsToRegistry Whether results of this query should be saved in the model registry. + * @param bool $distinct only select unique rows */ public function __construct(null|WhereCondition|array|WhereGroup $where = null, null|array $order = null, null|array $fields = null, null|Limit|array|int $limit = null, null|array $group = null, - protected bool $saveResultsToRegistry = true) + protected bool $saveResultsToRegistry = true, + protected bool $distinct = false, + ) { if ($where) { $this->where($where); @@ -100,6 +103,26 @@ public function getGroup(): ?array return $this->group; } + /** + * @param bool $distinct + * @return $this + */ + public function distinct(bool $distinct = true): static + { + $this->distinct = $distinct; + return $this; + } + + /** + * Get whether this query should be distinct + * + * @return bool + */ + public function isDistinct(): bool + { + return $this->distinct; + } + /** * Set whether results of this query should be saved in the model registry * diff --git a/test/tests/SQLTest.php b/test/tests/SQLTest.php index 10c0444..ed1a2c6 100644 --- a/test/tests/SQLTest.php +++ b/test/tests/SQLTest.php @@ -310,6 +310,14 @@ public function testSelectGroup() $this->assertEquals("SELECT * FROM `test` GROUP BY `number`, `text`", $this->sql->generate($query)); } + public function testSelectDistinct() + { + $query = new SelectQuery()->distinct(); + $query->modelClassName = TestModel::class; + + $this->assertEquals("SELECT DISTINCT * FROM `test`", $this->sql->generate($query)); + } + public function testDelete() { $query = new DeleteQuery(); diff --git a/test/tests/TestDriverTest.php b/test/tests/TestDriverTest.php index 4f2a0bb..04454f3 100644 --- a/test/tests/TestDriverTest.php +++ b/test/tests/TestDriverTest.php @@ -12,6 +12,7 @@ use Aternos\Model\Query\MaxField; use Aternos\Model\Query\MinField; use Aternos\Model\Query\SelectField; +use Aternos\Model\Query\SelectQuery; use Aternos\Model\Query\SumField; use Aternos\Model\Query\WhereCondition; use Aternos\Model\Query\WhereGroup; @@ -564,6 +565,35 @@ public function testOrderBeforeLimit(): void $this->assertEquals(7, $result[2]->number); } + public function testSelectDistinct(): void + { + $result = TestModel::query(new SelectQuery()->distinct()); + + $this->assertEquals(10, $result->count()); + for ($i = 0; $i < 10; $i++) { + $this->assertEquals($i, $result[$i]->number); + } + } + + public function testSelectDistinctField(): void + { + $testData = "FGHIJABCDE"; + foreach (str_split($testData) as $i => $char) { + TestModel::addTestEntry([ + "id" => $i . $char, + "text" => $char, + "number" => $i + ]); + } + + $result = TestModel::query(new SelectQuery()->distinct()->fields(["number"])); + + $this->assertEquals(10, $result->count()); + for ($i = 0; $i < 10; $i++) { + $this->assertEquals($i, $result[$i]->number); + } + } + protected function tearDown(): void { TestModel::clearTestEntries(); From e269374ecc7aa284bddcba0af876b9c585c70683 Mon Sep 17 00:00:00 2001 From: Julian Vennen Date: Tue, 17 Mar 2026 20:48:59 +0100 Subject: [PATCH 2/9] Improve tests for distinct queries in test driver --- test/tests/TestDriverTest.php | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/test/tests/TestDriverTest.php b/test/tests/TestDriverTest.php index 04454f3..4475aff 100644 --- a/test/tests/TestDriverTest.php +++ b/test/tests/TestDriverTest.php @@ -567,17 +567,31 @@ public function testOrderBeforeLimit(): void public function testSelectDistinct(): void { + TestModel::clearTestEntries(); + + $testData = "AABCCDDDEEFG"; + foreach (str_split($testData) as $i => $char) { + TestModel::addTestEntry([ + "id" => $i . $char, + "text" => $char, + "number" => $i + ]); + } + $result = TestModel::query(new SelectQuery()->distinct()); - $this->assertEquals(10, $result->count()); - for ($i = 0; $i < 10; $i++) { + $this->assertEquals(12, $result->count()); + for ($i = 0; $i < 12; $i++) { $this->assertEquals($i, $result[$i]->number); + $this->assertEquals($testData[$i], $result[$i]->text); } } public function testSelectDistinctField(): void { - $testData = "FGHIJABCDE"; + TestModel::clearTestEntries(); + + $testData = "AABCCDDDEEFG"; foreach (str_split($testData) as $i => $char) { TestModel::addTestEntry([ "id" => $i . $char, @@ -586,12 +600,16 @@ public function testSelectDistinctField(): void ]); } - $result = TestModel::query(new SelectQuery()->distinct()->fields(["number"])); + $result = TestModel::query(new SelectQuery()->distinct()->fields(["text"])); - $this->assertEquals(10, $result->count()); - for ($i = 0; $i < 10; $i++) { - $this->assertEquals($i, $result[$i]->number); - } + $this->assertEquals(7, $result->count()); + $this->assertEquals("A", $result[0]->text); + $this->assertEquals("B", $result[1]->text); + $this->assertEquals("C", $result[2]->text); + $this->assertEquals("D", $result[3]->text); + $this->assertEquals("E", $result[4]->text); + $this->assertEquals("F", $result[5]->text); + $this->assertEquals("G", $result[6]->text); } protected function tearDown(): void From 9caa4dcf368fcaf1b916d909ba823f5c8c654156 Mon Sep 17 00:00:00 2001 From: Julian Vennen Date: Tue, 17 Mar 2026 20:55:03 +0100 Subject: [PATCH 3/9] Fix distinct selects with limits --- src/Driver/Test/TestTable.php | 56 +++++++++++++++++++++++------------ test/tests/TestDriverTest.php | 23 ++++++++++++++ 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/src/Driver/Test/TestTable.php b/src/Driver/Test/TestTable.php index 83e21ba..8e5367d 100644 --- a/src/Driver/Test/TestTable.php +++ b/src/Driver/Test/TestTable.php @@ -6,6 +6,7 @@ use Aternos\Model\ModelRegistry; use Aternos\Model\Query\DeleteQuery; use Aternos\Model\Query\Direction; +use Aternos\Model\Query\Field; use Aternos\Model\Query\GroupField; use Aternos\Model\Query\OrderField; use Aternos\Model\Query\Query; @@ -57,7 +58,15 @@ public function addEntry(TestTableEntry ...$entry): static */ public function query(Query $query): QueryResult { - $entries = $this->findEntries($query->getWhere(), $query->getLimit()?->start, $query->getLimit()?->length, $query->getOrder()); + $distinct = $query instanceof SelectQuery && $query->isDistinct(); + $entries = $this->findEntries( + $query->getWhere(), + $query->getLimit()?->start, + $query->getLimit()?->length, + $query->getOrder(), + $distinct, + $query->getFields(), + ); if ($query instanceof SelectQuery) { $clonedEntries = []; @@ -74,23 +83,6 @@ public function query(Query $query): QueryResult $modelClass = $query->modelClassName; $entryData = $entry->getDataForFields($query->getFields()); $model = $modelClass::getModelFromData($entryData); - - if ($query->isDistinct()) { - foreach ($queryResult as $item) { - $same = true; - foreach ($entryData as $key => $value) { - if ($item->getField($key) !== $model->getField($key)) { - $same = false; - break; - } - } - - if ($same) { - continue 2; - } - } - } - $queryResult->add($model); } if ($query instanceof UpdateQuery) { @@ -146,9 +138,18 @@ public function groupAndAggregateEntries(array $entries, ?array $group, ?array $ * @param int|null $offset * @param int|null $limit * @param array|null $order + * @param bool $distinct + * @param Field[]|null $fields * @return TestTableEntry[] */ - protected function findEntries(?WhereGroup $where, ?int $offset = null, ?int $limit = null, ?array $order = null): array + protected function findEntries( + ?WhereGroup $where, + ?int $offset = null, + ?int $limit = null, + ?array $order = null, + bool $distinct = false, + ?array $fields = null, + ): array { $entries = []; if ($offset === null) { @@ -158,6 +159,23 @@ protected function findEntries(?WhereGroup $where, ?int $offset = null, ?int $li if (!$entry->matchesWhereGroup($where)) { continue; } + + if ($distinct) { + foreach ($entries as $item) { + $same = true; + foreach ($entry->getDataForFields($fields) as $key => $value) { + if ($item->getField($key) !== $entry->getField($key)) { + $same = false; + break; + } + } + + if ($same) { + continue 2; + } + } + } + $entries[] = $entry; } diff --git a/test/tests/TestDriverTest.php b/test/tests/TestDriverTest.php index 4475aff..c28ad9c 100644 --- a/test/tests/TestDriverTest.php +++ b/test/tests/TestDriverTest.php @@ -612,6 +612,29 @@ public function testSelectDistinctField(): void $this->assertEquals("G", $result[6]->text); } + public function testSelectDistinctBeforeLimit(): void + { + TestModel::clearTestEntries(); + + $testData = "AABCCDDDEEFG"; + foreach (str_split($testData) as $i => $char) { + TestModel::addTestEntry([ + "id" => $i . $char, + "text" => $char, + "number" => $i + ]); + } + + $result = TestModel::query(new SelectQuery()->distinct()->fields(["text"])->limit(5)); + + $this->assertEquals(5, $result->count()); + $this->assertEquals("A", $result[0]->text); + $this->assertEquals("B", $result[1]->text); + $this->assertEquals("C", $result[2]->text); + $this->assertEquals("D", $result[3]->text); + $this->assertEquals("E", $result[4]->text); + } + protected function tearDown(): void { TestModel::clearTestEntries(); From 0ff4ec4a52293613712b0b6c3f9f57935c6244d1 Mon Sep 17 00:00:00 2001 From: Julian Vennen Date: Tue, 17 Mar 2026 21:01:33 +0100 Subject: [PATCH 4/9] Include null fields in distinctness determination --- src/Driver/Test/TestTable.php | 4 ++-- src/Driver/Test/TestTableEntry.php | 4 ++-- test/tests/TestDriverTest.php | 31 ++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/Driver/Test/TestTable.php b/src/Driver/Test/TestTable.php index 8e5367d..cb5aba3 100644 --- a/src/Driver/Test/TestTable.php +++ b/src/Driver/Test/TestTable.php @@ -163,8 +163,8 @@ protected function findEntries( if ($distinct) { foreach ($entries as $item) { $same = true; - foreach ($entry->getDataForFields($fields) as $key => $value) { - if ($item->getField($key) !== $entry->getField($key)) { + foreach ($entry->getDataForFields($fields, true) as $key => $value) { + if ($item->getField($key) !== $value) { $same = false; break; } diff --git a/src/Driver/Test/TestTableEntry.php b/src/Driver/Test/TestTableEntry.php index b9e6dba..a136a1d 100644 --- a/src/Driver/Test/TestTableEntry.php +++ b/src/Driver/Test/TestTableEntry.php @@ -98,7 +98,7 @@ protected function matchesLike(string $likePattern, string $value): bool * @param SelectField[]|null $fields * @return array */ - public function getDataForFields(?array $fields): array + public function getDataForFields(?array $fields, bool $includeNull = false): array { if ($fields === null) { return $this->data; @@ -106,7 +106,7 @@ public function getDataForFields(?array $fields): array $data = []; foreach ($fields as $field) { $key = $field->alias ?? $field->key; - if (isset($this->data[$key])) { + if (isset($this->data[$key]) || $includeNull) { $data[$key] = $this->data[$key]; } } diff --git a/test/tests/TestDriverTest.php b/test/tests/TestDriverTest.php index c28ad9c..4f35eb3 100644 --- a/test/tests/TestDriverTest.php +++ b/test/tests/TestDriverTest.php @@ -635,6 +635,37 @@ public function testSelectDistinctBeforeLimit(): void $this->assertEquals("E", $result[4]->text); } + public function testSelectDistinctWithNull(): void + { + TestModel::clearTestEntries(); + + $testData = "AABCCDDDEEFG"; + foreach (str_split($testData) as $i => $char) { + TestModel::addTestEntry([ + "id" => $i . $char, + "text" => $char, + "number" => $i + ]); + } + TestModel::addTestEntry([ + "id" => "101", + "text" => null, + "number" => 101 + ]); + + $result = TestModel::query(new SelectQuery()->distinct()->fields(["text"])); + + $this->assertEquals(8, $result->count()); + $this->assertEquals("A", $result[0]->text); + $this->assertEquals("B", $result[1]->text); + $this->assertEquals("C", $result[2]->text); + $this->assertEquals("D", $result[3]->text); + $this->assertEquals("E", $result[4]->text); + $this->assertEquals("F", $result[5]->text); + $this->assertEquals("G", $result[6]->text); + $this->assertEquals(null, $result[7]->text); + } + protected function tearDown(): void { TestModel::clearTestEntries(); From c0b3e157842548d68b28c569430cedca56552657 Mon Sep 17 00:00:00 2001 From: Julian Vennen Date: Tue, 17 Mar 2026 21:10:55 +0100 Subject: [PATCH 5/9] Better null handling --- src/Driver/Test/TestTable.php | 8 +++++--- src/Driver/Test/TestTableEntry.php | 4 ++-- test/tests/TestDriverTest.php | 33 ++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/Driver/Test/TestTable.php b/src/Driver/Test/TestTable.php index cb5aba3..0a4aedc 100644 --- a/src/Driver/Test/TestTable.php +++ b/src/Driver/Test/TestTable.php @@ -151,6 +151,7 @@ protected function findEntries( ?array $fields = null, ): array { + /** @var TestTableEntry[] $entries */ $entries = []; if ($offset === null) { $offset = 0; @@ -160,11 +161,12 @@ protected function findEntries( continue; } - if ($distinct) { + if ($distinct && $fields) { foreach ($entries as $item) { $same = true; - foreach ($entry->getDataForFields($fields, true) as $key => $value) { - if ($item->getField($key) !== $value) { + foreach ($fields as $field) { + $key = $field->key; + if ($item->getField($key) !== $entry->getField($key)) { $same = false; break; } diff --git a/src/Driver/Test/TestTableEntry.php b/src/Driver/Test/TestTableEntry.php index a136a1d..b9e6dba 100644 --- a/src/Driver/Test/TestTableEntry.php +++ b/src/Driver/Test/TestTableEntry.php @@ -98,7 +98,7 @@ protected function matchesLike(string $likePattern, string $value): bool * @param SelectField[]|null $fields * @return array */ - public function getDataForFields(?array $fields, bool $includeNull = false): array + public function getDataForFields(?array $fields): array { if ($fields === null) { return $this->data; @@ -106,7 +106,7 @@ public function getDataForFields(?array $fields, bool $includeNull = false): arr $data = []; foreach ($fields as $field) { $key = $field->alias ?? $field->key; - if (isset($this->data[$key]) || $includeNull) { + if (isset($this->data[$key])) { $data[$key] = $this->data[$key]; } } diff --git a/test/tests/TestDriverTest.php b/test/tests/TestDriverTest.php index 4f35eb3..3c8dda6 100644 --- a/test/tests/TestDriverTest.php +++ b/test/tests/TestDriverTest.php @@ -666,6 +666,39 @@ public function testSelectDistinctWithNull(): void $this->assertEquals(null, $result[7]->text); } + public function testSelectDistinctWithNullAlias(): void + { + TestModel::clearTestEntries(); + + $testData = "AABCCDDDEEFG"; + foreach (str_split($testData) as $i => $char) { + TestModel::addTestEntry([ + "id" => $i . $char, + "text" => $char, + "number" => $i + ]); + } + TestModel::addTestEntry([ + "id" => "101", + "text" => null, + "number" => 101 + ]); + + $result = TestModel::query(new SelectQuery()->distinct()->fields([ + new SelectField("text")->setAlias("text_alias") + ])); + + $this->assertEquals(8, $result->count()); + $this->assertEquals("A", $result[0]->getField("text_alias")); + $this->assertEquals("B", $result[1]->getField("text_alias")); + $this->assertEquals("C", $result[2]->getField("text_alias")); + $this->assertEquals("D", $result[3]->getField("text_alias")); + $this->assertEquals("E", $result[4]->getField("text_alias")); + $this->assertEquals("F", $result[5]->getField("text_alias")); + $this->assertEquals("G", $result[6]->getField("text_alias")); + $this->assertEquals(null, $result[7]->getField("text_alias")); + } + protected function tearDown(): void { TestModel::clearTestEntries(); From 682f450aa381a08d613122c63a6f04450f98f5a3 Mon Sep 17 00:00:00 2001 From: Julian Vennen Date: Tue, 17 Mar 2026 22:37:42 +0100 Subject: [PATCH 6/9] Fix order of operations in test driver --- src/Driver/Test/TestTable.php | 80 ++++++++++++++--------------------- 1 file changed, 32 insertions(+), 48 deletions(-) diff --git a/src/Driver/Test/TestTable.php b/src/Driver/Test/TestTable.php index 0a4aedc..36b18ec 100644 --- a/src/Driver/Test/TestTable.php +++ b/src/Driver/Test/TestTable.php @@ -6,7 +6,6 @@ use Aternos\Model\ModelRegistry; use Aternos\Model\Query\DeleteQuery; use Aternos\Model\Query\Direction; -use Aternos\Model\Query\Field; use Aternos\Model\Query\GroupField; use Aternos\Model\Query\OrderField; use Aternos\Model\Query\Query; @@ -58,15 +57,7 @@ public function addEntry(TestTableEntry ...$entry): static */ public function query(Query $query): QueryResult { - $distinct = $query instanceof SelectQuery && $query->isDistinct(); - $entries = $this->findEntries( - $query->getWhere(), - $query->getLimit()?->start, - $query->getLimit()?->length, - $query->getOrder(), - $distinct, - $query->getFields(), - ); + $entries = $this->findEntries($query->getWhere()); if ($query instanceof SelectQuery) { $clonedEntries = []; @@ -74,8 +65,37 @@ public function query(Query $query): QueryResult $clonedEntries[] = clone $entry; } $entries = $this->groupAndAggregateEntries($clonedEntries, $query->getGroup(), $query->getFields()); + + if ($query->isDistinct() && $query->getFields()) { + $distinctEntries = []; + foreach ($entries as $entry) { + foreach ($distinctEntries as $distinctEntry) { + $same = true; + foreach ($query->getFields() as $field) { + $key = $field->key; + if ($distinctEntry->getField($key) !== $entry->getField($key)) { + $same = false; + break; + } + } + + if ($same) { + continue 2; + } + } + + $distinctEntries[] = clone $entry; + } + $entries = $distinctEntries; + } } + if ($query->getOrder() !== null) { + $entries = $this->orderEntries($entries, $query->getOrder()); + } + + $entries = array_slice($entries, $query->getLimit()?->start ?? 0, $query->getLimit()?->length); + $queryResult = new QueryResult(); foreach ($entries as $entry) { if ($query instanceof SelectQuery) { @@ -135,57 +155,21 @@ public function groupAndAggregateEntries(array $entries, ?array $group, ?array $ /** * @param WhereGroup|null $where - * @param int|null $offset - * @param int|null $limit - * @param array|null $order - * @param bool $distinct - * @param Field[]|null $fields * @return TestTableEntry[] */ - protected function findEntries( - ?WhereGroup $where, - ?int $offset = null, - ?int $limit = null, - ?array $order = null, - bool $distinct = false, - ?array $fields = null, - ): array + protected function findEntries(?WhereGroup $where): array { /** @var TestTableEntry[] $entries */ $entries = []; - if ($offset === null) { - $offset = 0; - } foreach ($this->entries as $entry) { if (!$entry->matchesWhereGroup($where)) { continue; } - if ($distinct && $fields) { - foreach ($entries as $item) { - $same = true; - foreach ($fields as $field) { - $key = $field->key; - if ($item->getField($key) !== $entry->getField($key)) { - $same = false; - break; - } - } - - if ($same) { - continue 2; - } - } - } - $entries[] = $entry; } - if ($order !== null) { - $entries = $this->orderEntries($entries, $order); - } - - return array_slice($entries, $offset, $limit); + return $entries; } /** From bdc0e43cfeae38be016391d96945116386fcf293 Mon Sep 17 00:00:00 2001 From: Julian Vennen Date: Tue, 17 Mar 2026 22:42:51 +0100 Subject: [PATCH 7/9] Add test for combining distinct and count --- test/tests/TestDriverTest.php | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/tests/TestDriverTest.php b/test/tests/TestDriverTest.php index 3c8dda6..4063b36 100644 --- a/test/tests/TestDriverTest.php +++ b/test/tests/TestDriverTest.php @@ -699,6 +699,40 @@ public function testSelectDistinctWithNullAlias(): void $this->assertEquals(null, $result[7]->getField("text_alias")); } + public function testSelectDistinctWithAggregateAndGroup(): void + { + TestModel::clearTestEntries(); + $testData = "AABCCDDDEEFG"; + foreach (str_split($testData) as $i => $char) { + TestModel::addTestEntry([ + "id" => $i . $char, + "text" => $char, + "number" => $i + ]); + } + $query = new SelectQuery() + ->distinct() + ->fields([ + "text", + new CountField(), + ]) + ->groupBy(["text"]); + $result = TestModel::query($query); + // There should be one row per distinct text value (A–G). + $this->assertEquals(7, $result->count()); + $countsByText = []; + foreach ($result as $row) { + $countsByText[$row->text] = $row->getField(CountField::COUNT_FIELD); + } + $this->assertEquals(2, $countsByText["A"]); + $this->assertEquals(1, $countsByText["B"]); + $this->assertEquals(2, $countsByText["C"]); + $this->assertEquals(3, $countsByText["D"]); + $this->assertEquals(2, $countsByText["E"]); + $this->assertEquals(1, $countsByText["F"]); + $this->assertEquals(1, $countsByText["G"]); + } + protected function tearDown(): void { TestModel::clearTestEntries(); From 9a65a491643f57c34aa9e90e369ec26b71ff3554 Mon Sep 17 00:00:00 2001 From: Julian Vennen Date: Tue, 17 Mar 2026 23:02:25 +0100 Subject: [PATCH 8/9] Fix grouping fields with aliases --- src/Driver/Test/TestTable.php | 2 +- test/tests/TestDriverTest.php | 24 +++++++++--------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/Driver/Test/TestTable.php b/src/Driver/Test/TestTable.php index 36b18ec..2c5cfe9 100644 --- a/src/Driver/Test/TestTable.php +++ b/src/Driver/Test/TestTable.php @@ -72,7 +72,7 @@ public function query(Query $query): QueryResult foreach ($distinctEntries as $distinctEntry) { $same = true; foreach ($query->getFields() as $field) { - $key = $field->key; + $key = $field->alias ?? $field->key; if ($distinctEntry->getField($key) !== $entry->getField($key)) { $same = false; break; diff --git a/test/tests/TestDriverTest.php b/test/tests/TestDriverTest.php index 4063b36..bb4180e 100644 --- a/test/tests/TestDriverTest.php +++ b/test/tests/TestDriverTest.php @@ -702,6 +702,7 @@ public function testSelectDistinctWithNullAlias(): void public function testSelectDistinctWithAggregateAndGroup(): void { TestModel::clearTestEntries(); + // Insert multiple rows with duplicate 'text' values $testData = "AABCCDDDEEFG"; foreach (str_split($testData) as $i => $char) { TestModel::addTestEntry([ @@ -710,27 +711,20 @@ public function testSelectDistinctWithAggregateAndGroup(): void "number" => $i ]); } + $query = new SelectQuery() ->distinct() ->fields([ - "text", new CountField(), ]) - ->groupBy(["text"]); + ->groupBy(["text"]) + ->orderBy([CountField::COUNT_FIELD => Direction::ASCENDING]); $result = TestModel::query($query); - // There should be one row per distinct text value (A–G). - $this->assertEquals(7, $result->count()); - $countsByText = []; - foreach ($result as $row) { - $countsByText[$row->text] = $row->getField(CountField::COUNT_FIELD); - } - $this->assertEquals(2, $countsByText["A"]); - $this->assertEquals(1, $countsByText["B"]); - $this->assertEquals(2, $countsByText["C"]); - $this->assertEquals(3, $countsByText["D"]); - $this->assertEquals(2, $countsByText["E"]); - $this->assertEquals(1, $countsByText["F"]); - $this->assertEquals(1, $countsByText["G"]); + + $this->assertEquals(3, $result->count()); + $this->assertEquals(1, $result[0]->getField(CountField::COUNT_FIELD)); + $this->assertEquals(2, $result[1]->getField(CountField::COUNT_FIELD)); + $this->assertEquals(3, $result[2]->getField(CountField::COUNT_FIELD)); } protected function tearDown(): void From 3cf283014c7e744ed4277b44519f33516e946814 Mon Sep 17 00:00:00 2001 From: Julian Vennen Date: Wed, 18 Mar 2026 09:16:40 +0100 Subject: [PATCH 9/9] Inline local variable again --- src/Driver/Test/TestTable.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Driver/Test/TestTable.php b/src/Driver/Test/TestTable.php index 2c5cfe9..8f1c753 100644 --- a/src/Driver/Test/TestTable.php +++ b/src/Driver/Test/TestTable.php @@ -101,8 +101,7 @@ public function query(Query $query): QueryResult if ($query instanceof SelectQuery) { /** @var class-string $modelClass */ $modelClass = $query->modelClassName; - $entryData = $entry->getDataForFields($query->getFields()); - $model = $modelClass::getModelFromData($entryData); + $model = $modelClass::getModelFromData($entry->getDataForFields($query->getFields())); $queryResult->add($model); } if ($query instanceof UpdateQuery) {