Passed
Pull Request — master (#172)
by David
02:56
created

FindObjectsQueryFactory::getPartialQuery()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
c 0
b 0
f 0
dl 0
loc 18
rs 9.9666
cc 3
nc 4
nop 3
1
<?php
2
declare(strict_types=1);
3
4
namespace TheCodingMachine\TDBM\QueryFactory;
5
6
use Doctrine\Common\Cache\Cache;
7
use Doctrine\DBAL\Platforms\MySqlPlatform;
8
use Doctrine\DBAL\Schema\Schema;
9
use Mouf\Database\MagicQuery;
10
use TheCodingMachine\TDBM\InnerResultArray;
11
use TheCodingMachine\TDBM\OrderByAnalyzer;
12
use TheCodingMachine\TDBM\QueryFactory\SmartEagerLoad\PartialQueryFactory;
13
use TheCodingMachine\TDBM\QueryFactory\SmartEagerLoad\Query\PartialQuery;
14
use TheCodingMachine\TDBM\QueryFactory\SmartEagerLoad\Query\StaticPartialQuery;
15
use TheCodingMachine\TDBM\QueryFactory\SmartEagerLoad\StorageNode;
16
use TheCodingMachine\TDBM\TDBMService;
17
use function implode;
18
19
/**
20
 * This class is in charge of creating the MagicQuery SQL based on parameters passed to findObjects method.
21
 */
22
class FindObjectsQueryFactory extends AbstractQueryFactory implements PartialQueryFactory
23
{
24
    private $additionalTablesFetch;
25
    private $filterString;
26
    /**
27
     * @var Cache
28
     */
29
    private $cache;
30
31
    public function __construct(string $mainTable, array $additionalTablesFetch, $filterString, $orderBy, TDBMService $tdbmService, Schema $schema, OrderByAnalyzer $orderByAnalyzer, Cache $cache)
32
    {
33
        parent::__construct($tdbmService, $schema, $orderByAnalyzer, $mainTable, $orderBy);
34
        $this->additionalTablesFetch = $additionalTablesFetch;
35
        $this->filterString = $filterString;
36
        $this->cache = $cache;
37
    }
38
39
    protected function compute(): void
40
    {
41
        $key = 'FindObjectsQueryFactory_'.$this->mainTable.'__'.implode('_/_', $this->additionalTablesFetch).'__'.$this->filterString.'__'.$this->orderBy;
42
        if ($this->cache->contains($key)) {
43
            [
44
                $this->magicSql,
45
                $this->magicSqlCount,
46
                $this->columnDescList,
47
                $this->magicSqlSubQuery
48
            ] = $this->cache->fetch($key);
49
            return;
50
        }
51
52
        list($columnDescList, $columnsList, $orderString) = $this->getColumnsList($this->mainTable, $this->additionalTablesFetch, $this->orderBy, true);
53
54
        $sql = 'SELECT DISTINCT '.implode(', ', $columnsList).' FROM MAGICJOIN('.$this->mainTable.')';
55
56
        $pkColumnNames = $this->tdbmService->getPrimaryKeyColumns($this->mainTable);
57
        $mysqlPlatform = new MySqlPlatform();
58
        $pkColumnNames = array_map(function ($pkColumn) use ($mysqlPlatform) {
59
            return $mysqlPlatform->quoteIdentifier($this->mainTable).'.'.$mysqlPlatform->quoteIdentifier($pkColumn);
60
        }, $pkColumnNames);
61
62
        $subQuery = 'SELECT DISTINCT '.implode(', ', $pkColumnNames).' FROM MAGICJOIN('.$this->mainTable.')';
63
64
        if (count($pkColumnNames) === 1 || $this->tdbmService->getConnection()->getDatabasePlatform() instanceof MySqlPlatform) {
65
            $countSql = 'SELECT COUNT(DISTINCT '.implode(', ', $pkColumnNames).') FROM MAGICJOIN('.$this->mainTable.')';
66
        } else {
67
            $countSql = 'SELECT COUNT(*) FROM ('.$subQuery.') tmp';
68
        }
69
70
        if (!empty($this->filterString)) {
71
            $sql .= ' WHERE '.$this->filterString;
72
            $countSql .= ' WHERE '.$this->filterString;
73
            $subQuery .= ' WHERE '.$this->filterString;
74
        }
75
76
        if (!empty($orderString)) {
77
            $sql .= ' ORDER BY '.$orderString;
78
        }
79
80
        $this->magicSql = $sql;
81
        $this->magicSqlCount = $countSql;
82
        $this->magicSqlSubQuery = $subQuery;
83
        $this->columnDescList = $columnDescList;
84
85
        $this->cache->save($key, [
86
            $this->magicSql,
87
            $this->magicSqlCount,
88
            $this->columnDescList,
89
            $this->magicSqlSubQuery,
90
        ]);
91
    }
92
93
    /**
94
     * Generates a SQL query to be used as a sub-query.
95
     * @param array<string, mixed> $parameters
96
     */
97
    public function getPartialQuery(StorageNode $storageNode, MagicQuery $magicQuery, array $parameters): PartialQuery
98
    {
99
        $mysqlPlatform = new MySqlPlatform();
100
101
        // Special case: if the main table is part of an inheritance relationship, we need to get all related tables
102
        $relatedTables = $this->tdbmService->_getRelatedTablesByInheritance($this->mainTable);
103
        if (count($relatedTables) === 1) {
104
            $sql = 'FROM '.$mysqlPlatform->quoteIdentifier($this->mainTable);
105
        } else {
106
            // Let's use MagicQuery to build the query
107
            $sql = 'FROM MAGICJOIN('.$this->mainTable.')';
108
        }
109
110
        if (!empty($this->filterString)) {
111
            $sql .= ' WHERE '.$this->filterString;
112
        }
113
114
        return new StaticPartialQuery($sql, $parameters, $relatedTables, $storageNode, $magicQuery);
115
    }
116
}
117