From cd9296adcc8aed4022af35e36ad05f7dd4e0aec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Radim=20Vaculi=CC=81k?= Date: Tue, 14 Jul 2026 09:43:08 +0200 Subject: [PATCH 1/2] =?UTF-8?q?Oprava=20po=C4=8Dtu=20=C5=99=C3=A1dk=C5=AF?= =?UTF-8?q?=20p=C5=99i=20GROUP=20BY=20p=C5=99es=20v=C3=ADce=20sloupc=C5=AF?= =?UTF-8?q?=20v=20Nette=20Database=20Table?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getCount() nahrazuje neplatné COUNT(DISTINCT sloupec1, sloupec2, ...) počítáním skupin přes poddotaz, takže vrací správný počet i když seskupovací sloupce obsahují NULL hodnoty. Řeší issue #991. --- .../NetteDatabaseTableDataSource.php | 22 +++++-------- .../NetteDatabaseTableDataSourceTest.phpt | 33 +++++++++++++++++++ 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/DataSource/NetteDatabaseTableDataSource.php b/src/DataSource/NetteDatabaseTableDataSource.php index 4a87cf960..def1db719 100755 --- a/src/DataSource/NetteDatabaseTableDataSource.php +++ b/src/DataSource/NetteDatabaseTableDataSource.php @@ -14,7 +14,6 @@ use Contributte\Datagrid\Utils\Sorting; use LogicException; use Nette\Database\Table\Selection; -use Nette\Utils\Strings; class NetteDatabaseTableDataSource extends FilterableDataSource implements IDataSource { @@ -29,25 +28,22 @@ public function getCount(): int { $dataSourceSqlBuilder = $this->dataSource->getSqlBuilder(); + if ($dataSourceSqlBuilder->getGroup() !== '') { + $query = sprintf('SELECT COUNT(*) FROM (%s) AS datagrid_count', $this->dataSource->getSql()); + + /** @phpstan-ignore argument.type */ + $result = $this->dataSource->getExplorer()->query($query, ...$dataSourceSqlBuilder->getParameters()); + + return (int) $result->fetchField(); + } + try { $primary = $this->dataSource->getPrimary(); } catch (LogicException) { - if ($dataSourceSqlBuilder->getGroup() !== '') { - return $this->dataSource->count( - 'DISTINCT ' . Strings::replace($dataSourceSqlBuilder->getGroup(), '~ (DESC|ASC)~') - ); - } - return $this->dataSource->count('*'); } - if ($dataSourceSqlBuilder->getGroup() !== '') { - return $this->dataSource->count( - 'DISTINCT ' . Strings::replace($dataSourceSqlBuilder->getGroup(), '~ (DESC|ASC)~') - ); - } - return $this->dataSource->count( $this->dataSource->getName() . '.' . (is_array($primary) ? reset($primary) : $primary) ); diff --git a/tests/Cases/DataSources/NetteDatabaseTableDataSourceTest.phpt b/tests/Cases/DataSources/NetteDatabaseTableDataSourceTest.phpt index c7d62f826..52312908f 100644 --- a/tests/Cases/DataSources/NetteDatabaseTableDataSourceTest.phpt +++ b/tests/Cases/DataSources/NetteDatabaseTableDataSourceTest.phpt @@ -10,6 +10,7 @@ use Nette\Database\Conventions\DiscoveredConventions; use Nette\Database\Explorer; use Nette\Database\Structure; use Nette\Database\Table\Selection; +use Tester\Assert; require __DIR__ . '/BaseDataSourceTest.phpt'; @@ -27,6 +28,38 @@ final class NetteDatabaseTableDataSourceTest extends BaseDataSourceTest $this->grid = $factory->createTestingDatagrid(); } + public function testGetCountWithGroupByMultipleColumns(): void + { + $this->db->query('CREATE TABLE orders_products ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title VARCHAR (50), + variant VARCHAR (50), + course_city VARCHAR (50), + course_date VARCHAR (50) + ); + '); + $this->db->getStructure()->rebuild(); + + $rows = [ + ['title' => 'A', 'variant' => 'x', 'course_city' => null, 'course_date' => null], + ['title' => 'A', 'variant' => 'x', 'course_city' => null, 'course_date' => null], + ['title' => 'B', 'variant' => 'y', 'course_city' => null, 'course_date' => null], + ['title' => 'C', 'variant' => 'z', 'course_city' => 'Prague', 'course_date' => '2021-01-01'], + ]; + + foreach ($rows as $row) { + $this->db->query('INSERT INTO orders_products', $row); + } + + $selection = $this->db->table('orders_products') + ->group('title, variant, course_city, course_date'); + + $ds = new NetteDatabaseTableDataSource($selection, 'id'); + + Assert::same(3, $ds->getCount()); + Assert::same(3, count($ds->getData())); + } + protected function setUpDatabase(): void { $connection = new Connection('sqlite::memory:'); From e7e14b5a98297f18ce7610bc15f3069c9656f9a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Radim=20Vaculi=CC=81k?= Date: Tue, 14 Jul 2026 09:50:25 +0200 Subject: [PATCH 2/2] =?UTF-8?q?Z=C3=ADsk=C3=A1n=C3=AD=20p=C5=99ipojen?= =?UTF-8?q?=C3=AD=20p=C5=99es=20NetteDatabaseSelectionHelper=20m=C3=ADsto?= =?UTF-8?q?=20getExplorer()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Metoda Selection::getExplorer() existuje až v nette/database 3.2.9, ale CI běží s 3.2.8. Použití zavedeného reflection helperu getContext() funguje napříč podporovanými verzemi. --- src/DataSource/NetteDatabaseTableDataSource.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/DataSource/NetteDatabaseTableDataSource.php b/src/DataSource/NetteDatabaseTableDataSource.php index def1db719..d46394b86 100755 --- a/src/DataSource/NetteDatabaseTableDataSource.php +++ b/src/DataSource/NetteDatabaseTableDataSource.php @@ -11,6 +11,7 @@ use Contributte\Datagrid\Filter\FilterSelect; use Contributte\Datagrid\Filter\FilterText; use Contributte\Datagrid\Utils\DateTimeHelper; +use Contributte\Datagrid\Utils\NetteDatabaseSelectionHelper; use Contributte\Datagrid\Utils\Sorting; use LogicException; use Nette\Database\Table\Selection; @@ -30,9 +31,10 @@ public function getCount(): int if ($dataSourceSqlBuilder->getGroup() !== '') { $query = sprintf('SELECT COUNT(*) FROM (%s) AS datagrid_count', $this->dataSource->getSql()); + $explorer = NetteDatabaseSelectionHelper::getContext($this->dataSource); /** @phpstan-ignore argument.type */ - $result = $this->dataSource->getExplorer()->query($query, ...$dataSourceSqlBuilder->getParameters()); + $result = $explorer->query($query, ...$dataSourceSqlBuilder->getParameters()); return (int) $result->fetchField(); }