FindObjectsFromSqlQueryFactory::fromCache()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 10
Ratio 100 %

Importance

Changes 0
Metric Value
dl 10
loc 10
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 2
1
<?php
2
3
namespace Mouf\Database\TDBM\QueryFactory;
4
5
use Doctrine\Common\Cache\Cache;
6
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
7
use Doctrine\DBAL\Schema\Schema;
8
use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer;
9
use Mouf\Database\TDBM\OrderByAnalyzer;
10
use Mouf\Database\TDBM\TDBMException;
11
use Mouf\Database\TDBM\TDBMService;
12
13
/**
14
 * This class is in charge of creating the MagicQuery SQL based on parameters passed to findObjectsFromSql method.
15
 */
16
class FindObjectsFromSqlQueryFactory extends AbstractQueryFactory
17
{
18
    private $mainTable;
19
    private $from;
20
    private $filterString;
21
    private $cache;
22
    private $cachePrefix;
23
24
    public function __construct(string $mainTable, string $from, $filterString, $orderBy, TDBMService $tdbmService, Schema $schema, OrderByAnalyzer $orderByAnalyzer, SchemaAnalyzer $schemaAnalyzer, Cache $cache, string $cachePrefix)
25
    {
26
        parent::__construct($tdbmService, $schema, $orderByAnalyzer, $orderBy);
27
        $this->mainTable = $mainTable;
28
        $this->from = $from;
29
        $this->filterString = $filterString;
30
        $this->schemaAnalyzer = $schemaAnalyzer;
0 ignored issues
show
Bug introduced by
The property schemaAnalyzer does not seem to exist. Did you mean schema?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
31
        $this->cache = $cache;
32
        $this->cachePrefix = $cachePrefix;
33
    }
34
35
    protected function compute()
36
    {
37
        $connection = $this->tdbmService->getConnection();
38
39
        $columnsList = null;
40
41
        $allFetchedTables = $this->tdbmService->_getRelatedTablesByInheritance($this->mainTable);
42
43
        $columnDescList = [];
44
45
        $tableGroupName = $this->getTableGroupName($allFetchedTables);
46
47
        foreach ($this->schema->getTable($this->mainTable)->getColumns() as $column) {
48
            $columnName = $column->getName();
49
            $columnDescList[] = [
50
                'as' => $columnName,
51
                'table' => $this->mainTable,
52
                'column' => $columnName,
53
                'type' => $column->getType(),
54
                'tableGroup' => $tableGroupName,
55
            ];
56
        }
57
58 View Code Duplication
        $sql = 'SELECT DISTINCT '.implode(', ', array_map(function ($columnDesc) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
59
            return $this->tdbmService->getConnection()->quoteIdentifier($this->mainTable).'.'.$this->tdbmService->getConnection()->quoteIdentifier($columnDesc['column']);
60
        }, $columnDescList)).' FROM '.$this->from;
61
62
        if (count($allFetchedTables) > 1) {
63
            list($columnDescList, $columnsList, $orderString) = $this->getColumnsList($this->mainTable, [], $this->orderBy);
64
        } elseif ($this->orderBy) {
65
            list(, , $orderString) = $this->getColumnsList($this->mainTable, [], $this->orderBy);
66
        }
67
68
        // Let's compute the COUNT.
69
        $pkColumnNames = $this->schema->getTable($this->mainTable)->getPrimaryKeyColumns();
70 View Code Duplication
        $pkColumnNames = array_map(function ($pkColumn) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
71
            return $this->tdbmService->getConnection()->quoteIdentifier($this->mainTable).'.'.$this->tdbmService->getConnection()->quoteIdentifier($pkColumn);
72
        }, $pkColumnNames);
73
74
        $countSql = 'SELECT COUNT(DISTINCT '.implode(', ', $pkColumnNames).') FROM '.$this->from;
75
76 View Code Duplication
        if (!empty($this->filterString)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
77
            $sql .= ' WHERE '.$this->filterString;
78
            $countSql .= ' WHERE '.$this->filterString;
79
        }
80
81
        if (!empty($orderString)) {
82
            $sql .= ' ORDER BY '.$orderString;
83
        }
84
85
        if (stripos($countSql, 'GROUP BY') !== false) {
86
            throw new TDBMException('Unsupported use of GROUP BY in SQL request.');
87
        }
88
89
        if ($columnsList !== null) {
90
            $joinSql = '';
91
            $parentFks = $this->getParentRelationshipForeignKeys($this->mainTable);
92 View Code Duplication
            foreach ($parentFks as $fk) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
93
                $joinSql .= sprintf(' JOIN %s ON (%s.%s = %s.%s)',
94
                    $connection->quoteIdentifier($fk->getForeignTableName()),
95
                    $connection->quoteIdentifier($fk->getLocalTableName()),
96
                    $connection->quoteIdentifier($fk->getLocalColumns()[0]),
97
                    $connection->quoteIdentifier($fk->getForeignTableName()),
98
                    $connection->quoteIdentifier($fk->getForeignColumns()[0])
99
                );
100
            }
101
102
            $childrenFks = $this->getChildrenRelationshipForeignKeys($this->mainTable);
103 View Code Duplication
            foreach ($childrenFks as $fk) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
104
                $joinSql .= sprintf(' LEFT JOIN %s ON (%s.%s = %s.%s)',
105
                    $connection->quoteIdentifier($fk->getLocalTableName()),
106
                    $connection->quoteIdentifier($fk->getForeignTableName()),
107
                    $connection->quoteIdentifier($fk->getForeignColumns()[0]),
108
                    $connection->quoteIdentifier($fk->getLocalTableName()),
109
                    $connection->quoteIdentifier($fk->getLocalColumns()[0])
110
                );
111
            }
112
113
            $sql = 'SELECT '.implode(', ', $columnsList).' FROM ('.$sql.') AS '.$this->mainTable.' '.$joinSql;
114
            if (!empty($orderString)) {
115
                $sql .= ' ORDER BY '.$orderString;
116
            }
117
        }
118
119
        $this->magicSql = $sql;
120
        $this->magicSqlCount = $countSql;
121
        $this->columnDescList = $columnDescList;
122
    }
123
124
    /**
125
     * @param string $tableName
126
     *
127
     * @return ForeignKeyConstraint[]
128
     */
129
    private function getParentRelationshipForeignKeys($tableName)
130
    {
131
        return $this->fromCache($this->cachePrefix.'_parentrelationshipfks_'.$tableName, function () use ($tableName) {
132
            return $this->getParentRelationshipForeignKeysWithoutCache($tableName);
133
        });
134
    }
135
136
    /**
137
     * @param string $tableName
138
     *
139
     * @return ForeignKeyConstraint[]
140
     */
141
    private function getParentRelationshipForeignKeysWithoutCache($tableName)
142
    {
143
        $parentFks = [];
144
        $currentTable = $tableName;
145
        while ($currentFk = $this->schemaAnalyzer->getParentRelationship($currentTable)) {
0 ignored issues
show
Bug introduced by
The property schemaAnalyzer does not seem to exist. Did you mean schema?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
146
            $currentTable = $currentFk->getForeignTableName();
147
            $parentFks[] = $currentFk;
148
        }
149
150
        return $parentFks;
151
    }
152
153
    /**
154
     * @param string $tableName
155
     *
156
     * @return ForeignKeyConstraint[]
157
     */
158
    private function getChildrenRelationshipForeignKeys(string $tableName) : array
159
    {
160
        return $this->fromCache($this->cachePrefix.'_childrenrelationshipfks_'.$tableName, function () use ($tableName) {
161
            return $this->getChildrenRelationshipForeignKeysWithoutCache($tableName);
162
        });
163
    }
164
165
    /**
166
     * @param string $tableName
167
     *
168
     * @return ForeignKeyConstraint[]
169
     */
170
    private function getChildrenRelationshipForeignKeysWithoutCache(string $tableName) : array
171
    {
172
        $children = $this->schemaAnalyzer->getChildrenRelationships($tableName);
0 ignored issues
show
Bug introduced by
The property schemaAnalyzer does not seem to exist. Did you mean schema?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
173
174
        if (!empty($children)) {
175
            $fksTables = array_map(function (ForeignKeyConstraint $fk) {
176
                return $this->getChildrenRelationshipForeignKeys($fk->getLocalTableName());
177
            }, $children);
178
179
            $fks = array_merge($children, call_user_func_array('array_merge', $fksTables));
180
181
            return $fks;
182
        } else {
183
            return [];
184
        }
185
    }
186
187
    /**
188
     * Returns an item from cache or computes it using $closure and puts it in cache.
189
     *
190
     * @param string   $key
191
     * @param callable $closure
192
     *
193
     * @return mixed
194
     */
195 View Code Duplication
    protected function fromCache(string $key, callable $closure)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
196
    {
197
        $item = $this->cache->fetch($key);
198
        if ($item === false) {
199
            $item = $closure();
200
            $this->cache->save($key, $item);
201
        }
202
203
        return $item;
204
    }
205
}
206