Skip to content

Commit 2d4e6f6

Browse files
authored
Merge pull request #200 from thecodingmachine/fix/findFromRaxSql-count
findFromRaw sql count
2 parents fd8845b + 9e04377 commit 2d4e6f6

File tree

5 files changed

+114
-7
lines changed

5 files changed

+114
-7
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ vendor/*
1313
/.travis/
1414
/vendor-bin/require-checker/vendor/
1515
/vendor-bin/couscous/vendor/
16-
.phpunit.result.cache
16+
.phpunit.result.cache
17+
tdbm.lock.yml

src/QueryFactory/FindObjectsFromRawSqlQueryFactory.php

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,14 +144,16 @@ private function processParsedUnionQuery(array $parsedSql, ?string $sqlCount): a
144144
private function processParsedSelectQuery(array $parsedSql, ?string $sqlCount): array
145145
{
146146
// 1: let's reformat the SELECT and construct our columns
147-
list($select, $columnDescriptors) = $this->formatSelect($parsedSql['SELECT']);
147+
list($select, $countSelect, $columnDescriptors) = $this->formatSelect($parsedSql['SELECT']);
148148
$generator = new PHPSQLCreator();
149149
$parsedSql['SELECT'] = $select;
150150
$processedSql = $generator->create($parsedSql);
151151

152152
// 2: let's compute the count query if needed
153153
if ($sqlCount === null) {
154-
$parsedSqlCount = $this->generateParsedSqlCount($parsedSql);
154+
$parsedCountSql = $parsedSql;
155+
$parsedCountSql['SELECT'] = $countSelect;
156+
$parsedSqlCount = $this->generateParsedSqlCount($parsedCountSql);
155157
$processedSqlCount = $generator->create($parsedSqlCount);
156158
} else {
157159
$processedSqlCount = $sqlCount;
@@ -173,40 +175,46 @@ private function formatSelect(array $baseSelect): array
173175

174176
$connection = $this->tdbmService->getConnection();
175177
$formattedSelect = [];
178+
$formattedCountSelect = [];
176179
$columnDescriptors = [];
177180
$fetchedTables = [];
178181

179182
foreach ($baseSelect as $entry) {
180183
if ($entry['expr_type'] !== 'colref') {
181184
$formattedSelect[] = $entry;
185+
$formattedCountSelect[] = $entry;
182186
continue;
183187
}
184188

185189
$noQuotes = $entry['no_quotes'];
186190
if ($noQuotes['delim'] !== '.' || count($noQuotes['parts']) !== 2) {
187191
$formattedSelect[] = $entry;
192+
$formattedCountSelect[] = $entry;
188193
continue;
189194
}
190195

191196
$tableName = $noQuotes['parts'][0];
192197
if (!in_array($tableName, $relatedTables)) {
193198
$formattedSelect[] = $entry;
199+
$formattedCountSelect[] = $entry;
194200
continue;
195201
}
196202

197203
$columnName = $noQuotes['parts'][1];
198204
if ($columnName !== '*') {
199205
$formattedSelect[] = $entry;
206+
$formattedCountSelect[] = $entry;
200207
continue;
201208
}
202209

203210
$table = $this->schema->getTable($tableName);
211+
$pkColumns = $table->getPrimaryKeyColumns();
204212
foreach ($table->getColumns() as $column) {
205213
$columnName = $column->getName();
206214
$alias = "{$tableName}____{$columnName}";
207-
$formattedSelect[] = [
215+
$astColumn = [
208216
'expr_type' => 'colref',
209-
'base_expr' => $connection->quoteIdentifier($tableName).'.'.$connection->quoteIdentifier($columnName),
217+
'base_expr' => $connection->quoteIdentifier($tableName) . '.' . $connection->quoteIdentifier($columnName),
210218
'no_quotes' => [
211219
'delim' => '.',
212220
'parts' => [
@@ -219,7 +227,10 @@ private function formatSelect(array $baseSelect): array
219227
'name' => $alias,
220228
]
221229
];
222-
230+
$formattedSelect[] = $astColumn;
231+
if (in_array($columnName, $pkColumns)) {
232+
$formattedCountSelect[] = $astColumn;
233+
}
223234
$columnDescriptors[$alias] = [
224235
'as' => $alias,
225236
'table' => $tableName,
@@ -241,7 +252,13 @@ private function formatSelect(array $baseSelect): array
241252
$formattedSelect[$i]['delim'] = ',';
242253
}
243254
}
244-
return [$formattedSelect, $columnDescriptors];
255+
256+
for ($i = 0; $i < count($formattedCountSelect) - 1; $i++) {
257+
if (!isset($formattedCountSelect[$i]['delim'])) {
258+
$formattedCountSelect[$i]['delim'] = ',';
259+
}
260+
}
261+
return [$formattedSelect, $formattedCountSelect, $columnDescriptors];
245262
}
246263

247264
/**

tests/Dao/TestAlbumDao.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace TheCodingMachine\TDBM\Dao;
5+
6+
use TheCodingMachine\TDBM\Test\Dao\Bean\AlbumBean;
7+
use TheCodingMachine\TDBM\Test\Dao\Generated\AlbumBaseDao;
8+
9+
/**
10+
* The AlbumDao class will maintain the persistence of UserBean class into the users table.
11+
*/
12+
class TestAlbumDao extends AlbumBaseDao
13+
{
14+
/**
15+
* @return \TheCodingMachine\TDBM\ResultIterator|AlbumBean[]
16+
*/
17+
public function findAllFromRawSql()
18+
{
19+
return $this->findFromRawSql('SELECT DISTINCT albums.* FROM albums');
20+
}
21+
22+
/**
23+
* @return \TheCodingMachine\TDBM\ResultIterator|AlbumBean[]
24+
*/
25+
public function findAllFromRawSqlWithCount()
26+
{
27+
return $this->findFromRawSql(
28+
'SELECT DISTINCT albums.* FROM albums',
29+
[],
30+
'SELECT COUNT(DISTINCT albums.id) FROM albums'
31+
);
32+
}
33+
}

tests/Dao/TestPersonDao.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
/*
3+
* This file has been automatically generated by TDBM.
4+
* You can edit this file as it will not be overwritten.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace TheCodingMachine\TDBM\Dao;
10+
11+
use TheCodingMachine\TDBM\ResultIterator;
12+
use TheCodingMachine\TDBM\Test\Dao\Generated\PersonBaseDao;
13+
14+
/**
15+
* The ContactDao class will maintain the persistence of ContactBean class into the contact table.
16+
*/
17+
class TestPersonDao extends PersonBaseDao
18+
{
19+
public function testFindFromRawSQLOnInherited(): ResultIterator
20+
{
21+
$sql = '
22+
SELECT DISTINCT person.*, contact.*, users.*
23+
FROM person JOIN contact ON person.id = contact.id
24+
JOIN users ON contact.id = users.id
25+
';
26+
27+
return $this->findFromRawSql($sql, []);
28+
}
29+
}

tests/TDBMDaoGeneratorTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@
3030
use Ramsey\Uuid\Uuid;
3131
use ReflectionClass;
3232
use ReflectionMethod;
33+
use TheCodingMachine\TDBM\Dao\TestAlbumDao;
3334
use TheCodingMachine\TDBM\Dao\TestArticleDao;
3435
use TheCodingMachine\TDBM\Dao\TestCountryDao;
36+
use TheCodingMachine\TDBM\Dao\TestPersonDao;
3537
use TheCodingMachine\TDBM\Dao\TestRoleDao;
3638
use TheCodingMachine\TDBM\Dao\TestUserDao;
3739
use TheCodingMachine\TDBM\Fixtures\Interfaces\TestUserDaoInterface;
@@ -42,6 +44,7 @@
4244
use TheCodingMachine\TDBM\Test\Dao\ArtistDao;
4345
use TheCodingMachine\TDBM\Test\Dao\BaseObjectDao;
4446
use TheCodingMachine\TDBM\Test\Dao\Bean\AccountBean;
47+
use TheCodingMachine\TDBM\Test\Dao\Bean\AlbumBean;
4548
use TheCodingMachine\TDBM\Test\Dao\Bean\AllNullableBean;
4649
use TheCodingMachine\TDBM\Test\Dao\Bean\AnimalBean;
4750
use TheCodingMachine\TDBM\Test\Dao\Bean\Article2Bean;
@@ -2178,4 +2181,28 @@ public function testFindByDateTime(): void
21782181
$personDao->findByModifiedAt(new \DateTimeImmutable())->count();
21792182
$this->assertTrue(true);
21802183
}
2184+
2185+
/**
2186+
* Bug: find from sql use a `COUNT(DISTINCT *)` which fails because of null values.
2187+
*/
2188+
public function testFindFromRawSqlCount(): void
2189+
{
2190+
$dao = new TestAlbumDao($this->tdbmService);
2191+
$albums = $dao->findAllFromRawSql();
2192+
2193+
$firstAlbum = $albums->first();
2194+
assert($firstAlbum instanceof AlbumBean);
2195+
$this->assertNull($firstAlbum->getNode()); // This null ensure reproducibility of the bug
2196+
$expectedCount = $dao->findAllFromRawSqlWithCount()->count();
2197+
$this->assertEquals($expectedCount, $albums->count());
2198+
}
2199+
2200+
public function testFindFromRawSQLOnInheritance(): void
2201+
{
2202+
$dao = new TestPersonDao($this->tdbmService);
2203+
$objects = $dao->testFindFromRawSQLOnInherited();
2204+
2205+
$this->assertNotNull($objects->first());
2206+
$this->assertEquals(6, $objects->count());
2207+
}
21812208
}

0 commit comments

Comments
 (0)