Skip to content

Commit ae1488f

Browse files
committed
Adding the ability to filter by ResultIterator at the top level of a filter
1 parent 2c868d0 commit ae1488f

10 files changed

+136
-9
lines changed

src/QueryFactory/AbstractQueryFactory.php

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,25 @@ abstract class AbstractQueryFactory implements QueryFactory
4747
*/
4848
protected $magicSqlSubQuery;
4949
protected $columnDescList;
50+
protected $subQueryColumnDescList;
51+
/**
52+
* @var string
53+
*/
54+
protected $mainTable;
5055

5156
/**
5257
* @param TDBMService $tdbmService
5358
* @param Schema $schema
5459
* @param OrderByAnalyzer $orderByAnalyzer
5560
* @param string|UncheckedOrderBy|null $orderBy
5661
*/
57-
public function __construct(TDBMService $tdbmService, Schema $schema, OrderByAnalyzer $orderByAnalyzer, $orderBy)
62+
public function __construct(TDBMService $tdbmService, Schema $schema, OrderByAnalyzer $orderByAnalyzer, string $mainTable, $orderBy)
5863
{
5964
$this->tdbmService = $tdbmService;
6065
$this->schema = $schema;
6166
$this->orderByAnalyzer = $orderByAnalyzer;
6267
$this->orderBy = $orderBy;
68+
$this->mainTable = $mainTable;
6369
}
6470

6571
/**
@@ -242,6 +248,27 @@ public function getColumnDescriptors() : array
242248
return $this->columnDescList;
243249
}
244250

251+
/**
252+
* @return string[][] An array of column descriptors. Value is an array with those keys: table, column
253+
*/
254+
public function getSubQueryColumnDescriptors() : array
255+
{
256+
if ($this->subQueryColumnDescList === null) {
257+
$columns = $this->tdbmService->getPrimaryKeyColumns($this->mainTable);
258+
$descriptors = [];
259+
foreach ($columns as $column) {
260+
$descriptors[] = [
261+
'table' => $this->mainTable,
262+
'column' => $column
263+
];
264+
}
265+
$this->subQueryColumnDescList = $descriptors;
266+
}
267+
268+
return $this->subQueryColumnDescList;
269+
}
270+
271+
245272
/**
246273
* Sets the ORDER BY directive executed in SQL.
247274
*

src/QueryFactory/FindObjectsFromRawSqlQueryFactory.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,4 +406,12 @@ public function getMagicSqlSubQuery(): string
406406
{
407407
throw new TDBMException('Using resultset generated from findFromRawSql as subqueries is unsupported for now.');
408408
}
409+
410+
/**
411+
* @return string[][] An array of column descriptors. Value is an array with those keys: table, column
412+
*/
413+
public function getSubQueryColumnDescriptors(): array
414+
{
415+
throw new TDBMException('Using resultset generated from findFromRawSql as subqueries is unsupported for now.');
416+
}
409417
}

src/QueryFactory/FindObjectsFromSqlQueryFactory.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
*/
1919
class FindObjectsFromSqlQueryFactory extends AbstractQueryFactory
2020
{
21-
private $mainTable;
2221
private $from;
2322
private $filterString;
2423
private $cache;
@@ -27,8 +26,7 @@ class FindObjectsFromSqlQueryFactory extends AbstractQueryFactory
2726

2827
public function __construct(string $mainTable, string $from, $filterString, $orderBy, TDBMService $tdbmService, Schema $schema, OrderByAnalyzer $orderByAnalyzer, SchemaAnalyzer $schemaAnalyzer, Cache $cache, string $cachePrefix)
2928
{
30-
parent::__construct($tdbmService, $schema, $orderByAnalyzer, $orderBy);
31-
$this->mainTable = $mainTable;
29+
parent::__construct($tdbmService, $schema, $orderByAnalyzer, $mainTable, $orderBy);
3230
$this->from = $from;
3331
$this->filterString = $filterString;
3432
$this->schemaAnalyzer = $schemaAnalyzer;

src/QueryFactory/FindObjectsQueryFactory.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*/
1616
class FindObjectsQueryFactory extends AbstractQueryFactory
1717
{
18-
private $mainTable;
1918
private $additionalTablesFetch;
2019
private $filterString;
2120
/**
@@ -25,8 +24,7 @@ class FindObjectsQueryFactory extends AbstractQueryFactory
2524

2625
public function __construct(string $mainTable, array $additionalTablesFetch, $filterString, $orderBy, TDBMService $tdbmService, Schema $schema, OrderByAnalyzer $orderByAnalyzer, Cache $cache)
2726
{
28-
parent::__construct($tdbmService, $schema, $orderByAnalyzer, $orderBy);
29-
$this->mainTable = $mainTable;
27+
parent::__construct($tdbmService, $schema, $orderByAnalyzer, $mainTable, $orderBy);
3028
$this->additionalTablesFetch = $additionalTablesFetch;
3129
$this->filterString = $filterString;
3230
$this->cache = $cache;

src/QueryFactory/QueryFactory.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,9 @@ public function getMagicSqlSubQuery() : string;
4242
* @return mixed[][] An array of column descriptors. The key is in the form "$tableName____$columnName". Value is an array with those keys: as, table, column, type, tableGroup
4343
*/
4444
public function getColumnDescriptors() : array;
45+
46+
/**
47+
* @return string[][] An array of column descriptors. Value is an array with those keys: table, column
48+
*/
49+
public function getSubQueryColumnDescriptors() : array;
4550
}

src/ResultIterator.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use function array_map;
88
use Doctrine\DBAL\Connection;
99
use Doctrine\DBAL\Statement;
10+
use function array_pop;
1011
use function is_array;
1112
use function is_int;
1213
use Mouf\Database\MagicQuery;
@@ -354,4 +355,24 @@ public function withParameters(array $parameters) : ResultIterator
354355

355356
return $clone;
356357
}
358+
359+
/**
360+
* @internal
361+
* @return string
362+
*/
363+
public function _getSubQuery(): string
364+
{
365+
$sql = $this->magicQuery->build($this->queryFactory->getMagicSqlSubQuery(), $this->parameters);
366+
$primaryKeyColumnDescs = $this->queryFactory->getSubQueryColumnDescriptors();
367+
368+
if (count($primaryKeyColumnDescs) > 1) {
369+
throw new TDBMException('You cannot use in a sub-query a table that has a primary key on more that 1 column.');
370+
}
371+
372+
$pkDesc = array_pop($primaryKeyColumnDescs);
373+
374+
$sql = $this->tdbmService->getConnection()->quoteIdentifier($pkDesc['table']).'.'.$this->tdbmService->getConnection()->quoteIdentifier($pkDesc['column']).' IN ('.$sql.')';
375+
376+
return $sql;
377+
}
357378
}

src/TDBMService.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,9 @@ public function buildFilterFromFilterBag($filter_bag, AbstractPlatform $platform
414414
}
415415

416416
return ['(' . implode(') AND (', $sqlParts) . ')', $parameters, $counter];
417+
} elseif ($filter_bag instanceof ResultIterator) {
418+
$subQuery = $filter_bag->_getSubQuery();
419+
return [$subQuery, [], $counter];
417420
} elseif ($filter_bag instanceof AbstractTDBMObject) {
418421
$sqlParts = [];
419422
$parameters = [];
@@ -433,6 +436,7 @@ public function buildFilterFromFilterBag($filter_bag, AbstractPlatform $platform
433436

434437
return [implode(' AND ', $sqlParts), $parameters, $counter];
435438
} elseif ($filter_bag instanceof \Iterator) {
439+
// TODO: we could instead check if is_iterable($filter_bag). That would remove useless code here.
436440
return $this->buildFilterFromFilterBag(iterator_to_array($filter_bag), $platform, $counter);
437441
} else {
438442
throw new TDBMException('Error in filter. An object has been passed that is neither a SQL string, nor an array, nor a bean, nor null.');

tests/Dao/TestArticleDao.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
namespace TheCodingMachine\TDBM\Dao;
55

6+
use TheCodingMachine\TDBM\ResultIterator;
67
use TheCodingMachine\TDBM\Test\Dao\Bean\ArticleBean;
78
use TheCodingMachine\TDBM\Test\Dao\Generated\ArticleBaseDao;
89

@@ -14,9 +15,9 @@ class TestArticleDao extends ArticleBaseDao
1415
/**
1516
* Used to test a findFromSql with an order by clause on an inherited table.
1617
*
17-
* @return ArticleBean[]
18+
* @return ResultIterator&ArticleBean[]
1819
*/
19-
public function getArticlesByUserLogin()
20+
public function getArticlesByUserLogin(): ResultIterator
2021
{
2122
return $this->findFromSql(
2223
'article JOIN users ON article.author_id = users.id',

tests/Dao/TestArticleSubQueryDao.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace TheCodingMachine\TDBM\Dao;
5+
6+
use TheCodingMachine\TDBM\ResultIterator;
7+
use TheCodingMachine\TDBM\TDBMService;
8+
use TheCodingMachine\TDBM\Test\Dao\Bean\ArticleBean;
9+
use TheCodingMachine\TDBM\Test\Dao\Generated\ArticleBaseDao;
10+
11+
/**
12+
* The UserDao class will maintain the persistence of UserBean class into the users table.
13+
*/
14+
class TestArticleSubQueryDao extends ArticleBaseDao
15+
{
16+
/**
17+
* @var TestUserDao
18+
*/
19+
private $userDao;
20+
21+
public function __construct(TDBMService $tdbmService, TestUserDao $userDao)
22+
{
23+
parent::__construct($tdbmService);
24+
$this->userDao = $userDao;
25+
}
26+
27+
/**
28+
* Used to test a findFromSql with an order by clause on an inherited table.
29+
*
30+
* @return ResultIterator&ArticleBean[]
31+
*/
32+
public function getArticlesByUserLoginStartingWith(string $login): ResultIterator
33+
{
34+
/*return $this->find(
35+
'author_id IN (:authorIds)',
36+
[ 'authorIds' => $this->userDao->getUsersByLoginStartingWith($login) ],
37+
'users.login'
38+
);*/
39+
return $this->find(
40+
$this->userDao->getUsersByLoginStartingWith($login),
41+
[],
42+
'users.login'
43+
);
44+
}
45+
}

tests/TDBMDaoGeneratorTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
use ReflectionClass;
3232
use ReflectionMethod;
3333
use TheCodingMachine\TDBM\Dao\TestArticleDao;
34+
use TheCodingMachine\TDBM\Dao\TestArticleSubQueryDao;
3435
use TheCodingMachine\TDBM\Dao\TestCountryDao;
3536
use TheCodingMachine\TDBM\Dao\TestRoleDao;
3637
use TheCodingMachine\TDBM\Dao\TestUserDao;
@@ -2184,4 +2185,23 @@ public function testSQLCountWithArray(): void
21842185
$users = $userDao->getUsersByComplexFilterBag($country, ['John Doe', 'John Smith'])->take(0, 1);
21852186
$this->assertEquals(1, $users->count());
21862187
}
2188+
2189+
/**
2190+
* @depends testDaoGeneration
2191+
*/
2192+
public function testSubQueryWithFind()
2193+
{
2194+
$userDao = new TestUserDao($this->tdbmService);
2195+
$articleDao = new TestArticleSubQueryDao($this->tdbmService, $userDao);
2196+
2197+
$bill = $userDao->getById(4);
2198+
$article = new ArticleBean('Foo');
2199+
$article->setAuthor($bill);
2200+
$articleDao->save($article);
2201+
2202+
$results = $articleDao->getArticlesByUserLoginStartingWith('bill');
2203+
2204+
$this->assertCount(1, $results);
2205+
$this->assertSame('Foo', $results[0]->getContent());
2206+
}
21872207
}

0 commit comments

Comments
 (0)