Completed
Pull Request — 4.2 (#111)
by David
03:28
created
src/Mouf/Database/TDBM/TDBMSchemaAnalyzer.php 2 patches
Indentation   +134 added lines, -134 removed lines patch added patch discarded remove patch
@@ -14,138 +14,138 @@
 block discarded – undo
14 14
  */
15 15
 class TDBMSchemaAnalyzer
16 16
 {
17
-    private $connection;
18
-
19
-    /**
20
-     * @var Schema
21
-     */
22
-    private $schema;
23
-
24
-    /**
25
-     * @var string
26
-     */
27
-    private $cachePrefix;
28
-
29
-    /**
30
-     * @var Cache
31
-     */
32
-    private $cache;
33
-
34
-    /**
35
-     * @var SchemaAnalyzer
36
-     */
37
-    private $schemaAnalyzer;
38
-
39
-    /**
40
-     * @param Connection     $connection     The DBAL DB connection to use
41
-     * @param Cache          $cache          A cache service to be used
42
-     * @param SchemaAnalyzer $schemaAnalyzer The schema analyzer that will be used to find shortest paths...
43
-     *                                       Will be automatically created if not passed
44
-     */
45
-    public function __construct(Connection $connection, Cache $cache, SchemaAnalyzer $schemaAnalyzer)
46
-    {
47
-        $this->connection = $connection;
48
-        $this->cache = $cache;
49
-        $this->schemaAnalyzer = $schemaAnalyzer;
50
-    }
51
-
52
-    /**
53
-     * Returns a unique ID for the current connection. Useful for namespacing cache entries in the current connection.
54
-     *
55
-     * @return string
56
-     */
57
-    public function getCachePrefix()
58
-    {
59
-        if ($this->cachePrefix === null) {
60
-            $this->cachePrefix = hash('md4', $this->connection->getHost().'-'.$this->connection->getPort().'-'.$this->connection->getDatabase().'-'.$this->connection->getDriver()->getName());
61
-        }
62
-
63
-        return $this->cachePrefix;
64
-    }
65
-
66
-    /**
67
-     * Returns the (cached) schema.
68
-     *
69
-     * @return Schema
70
-     */
71
-    public function getSchema()
72
-    {
73
-        if ($this->schema === null) {
74
-            $cacheKey = $this->getCachePrefix().'_schema';
75
-            if ($this->cache->contains($cacheKey)) {
76
-                $this->schema = $this->cache->fetch($cacheKey);
77
-            } else {
78
-                $this->schema = $this->connection->getSchemaManager()->createSchema();
79
-                $this->cache->save($cacheKey, $this->schema);
80
-            }
81
-        }
82
-
83
-        return $this->schema;
84
-    }
85
-
86
-    /**
87
-     * Returns the list of pivot tables linked to table $tableName.
88
-     *
89
-     * @param string $tableName
90
-     *
91
-     * @return array|string[]
92
-     */
93
-    public function getPivotTableLinkedToTable($tableName)
94
-    {
95
-        $cacheKey = $this->getCachePrefix().'_pivottables_link_'.$tableName;
96
-        if ($this->cache->contains($cacheKey)) {
97
-            return $this->cache->fetch($cacheKey);
98
-        }
99
-
100
-        $pivotTables = [];
101
-
102
-        $junctionTables = $this->schemaAnalyzer->detectJunctionTables(true);
103
-        foreach ($junctionTables as $table) {
104
-            $fks = $table->getForeignKeys();
105
-            foreach ($fks as $fk) {
106
-                if ($fk->getForeignTableName() == $tableName) {
107
-                    $pivotTables[] = $table->getName();
108
-                    break;
109
-                }
110
-            }
111
-        }
112
-
113
-        $this->cache->save($cacheKey, $pivotTables);
114
-
115
-        return $pivotTables;
116
-    }
117
-
118
-    /**
119
-     * Returns the list of foreign keys pointing to the table represented by this bean, excluding foreign keys
120
-     * from junction tables and from inheritance.
121
-     *
122
-     * @return ForeignKeyConstraint[]
123
-     */
124
-    public function getIncomingForeignKeys($tableName)
125
-    {
126
-        $junctionTables = $this->schemaAnalyzer->detectJunctionTables(true);
127
-        $junctionTableNames = array_map(function (Table $table) {
128
-            return $table->getName();
129
-        }, $junctionTables);
130
-        $childrenRelationships = $this->schemaAnalyzer->getChildrenRelationships($tableName);
131
-
132
-        $fks = [];
133
-        foreach ($this->getSchema()->getTables() as $table) {
134
-            foreach ($table->getForeignKeys() as $fk) {
135
-                if ($fk->getForeignTableName() === $tableName) {
136
-                    if (in_array($fk->getLocalTableName(), $junctionTableNames)) {
137
-                        continue;
138
-                    }
139
-                    foreach ($childrenRelationships as $childFk) {
140
-                        if ($fk->getLocalTableName() === $childFk->getLocalTableName() && $fk->getLocalColumns() === $childFk->getLocalColumns()) {
141
-                            continue 2;
142
-                        }
143
-                    }
144
-                    $fks[] = $fk;
145
-                }
146
-            }
147
-        }
148
-
149
-        return $fks;
150
-    }
17
+	private $connection;
18
+
19
+	/**
20
+	 * @var Schema
21
+	 */
22
+	private $schema;
23
+
24
+	/**
25
+	 * @var string
26
+	 */
27
+	private $cachePrefix;
28
+
29
+	/**
30
+	 * @var Cache
31
+	 */
32
+	private $cache;
33
+
34
+	/**
35
+	 * @var SchemaAnalyzer
36
+	 */
37
+	private $schemaAnalyzer;
38
+
39
+	/**
40
+	 * @param Connection     $connection     The DBAL DB connection to use
41
+	 * @param Cache          $cache          A cache service to be used
42
+	 * @param SchemaAnalyzer $schemaAnalyzer The schema analyzer that will be used to find shortest paths...
43
+	 *                                       Will be automatically created if not passed
44
+	 */
45
+	public function __construct(Connection $connection, Cache $cache, SchemaAnalyzer $schemaAnalyzer)
46
+	{
47
+		$this->connection = $connection;
48
+		$this->cache = $cache;
49
+		$this->schemaAnalyzer = $schemaAnalyzer;
50
+	}
51
+
52
+	/**
53
+	 * Returns a unique ID for the current connection. Useful for namespacing cache entries in the current connection.
54
+	 *
55
+	 * @return string
56
+	 */
57
+	public function getCachePrefix()
58
+	{
59
+		if ($this->cachePrefix === null) {
60
+			$this->cachePrefix = hash('md4', $this->connection->getHost().'-'.$this->connection->getPort().'-'.$this->connection->getDatabase().'-'.$this->connection->getDriver()->getName());
61
+		}
62
+
63
+		return $this->cachePrefix;
64
+	}
65
+
66
+	/**
67
+	 * Returns the (cached) schema.
68
+	 *
69
+	 * @return Schema
70
+	 */
71
+	public function getSchema()
72
+	{
73
+		if ($this->schema === null) {
74
+			$cacheKey = $this->getCachePrefix().'_schema';
75
+			if ($this->cache->contains($cacheKey)) {
76
+				$this->schema = $this->cache->fetch($cacheKey);
77
+			} else {
78
+				$this->schema = $this->connection->getSchemaManager()->createSchema();
79
+				$this->cache->save($cacheKey, $this->schema);
80
+			}
81
+		}
82
+
83
+		return $this->schema;
84
+	}
85
+
86
+	/**
87
+	 * Returns the list of pivot tables linked to table $tableName.
88
+	 *
89
+	 * @param string $tableName
90
+	 *
91
+	 * @return array|string[]
92
+	 */
93
+	public function getPivotTableLinkedToTable($tableName)
94
+	{
95
+		$cacheKey = $this->getCachePrefix().'_pivottables_link_'.$tableName;
96
+		if ($this->cache->contains($cacheKey)) {
97
+			return $this->cache->fetch($cacheKey);
98
+		}
99
+
100
+		$pivotTables = [];
101
+
102
+		$junctionTables = $this->schemaAnalyzer->detectJunctionTables(true);
103
+		foreach ($junctionTables as $table) {
104
+			$fks = $table->getForeignKeys();
105
+			foreach ($fks as $fk) {
106
+				if ($fk->getForeignTableName() == $tableName) {
107
+					$pivotTables[] = $table->getName();
108
+					break;
109
+				}
110
+			}
111
+		}
112
+
113
+		$this->cache->save($cacheKey, $pivotTables);
114
+
115
+		return $pivotTables;
116
+	}
117
+
118
+	/**
119
+	 * Returns the list of foreign keys pointing to the table represented by this bean, excluding foreign keys
120
+	 * from junction tables and from inheritance.
121
+	 *
122
+	 * @return ForeignKeyConstraint[]
123
+	 */
124
+	public function getIncomingForeignKeys($tableName)
125
+	{
126
+		$junctionTables = $this->schemaAnalyzer->detectJunctionTables(true);
127
+		$junctionTableNames = array_map(function (Table $table) {
128
+			return $table->getName();
129
+		}, $junctionTables);
130
+		$childrenRelationships = $this->schemaAnalyzer->getChildrenRelationships($tableName);
131
+
132
+		$fks = [];
133
+		foreach ($this->getSchema()->getTables() as $table) {
134
+			foreach ($table->getForeignKeys() as $fk) {
135
+				if ($fk->getForeignTableName() === $tableName) {
136
+					if (in_array($fk->getLocalTableName(), $junctionTableNames)) {
137
+						continue;
138
+					}
139
+					foreach ($childrenRelationships as $childFk) {
140
+						if ($fk->getLocalTableName() === $childFk->getLocalTableName() && $fk->getLocalColumns() === $childFk->getLocalColumns()) {
141
+							continue 2;
142
+						}
143
+					}
144
+					$fks[] = $fk;
145
+				}
146
+			}
147
+		}
148
+
149
+		return $fks;
150
+	}
151 151
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -124,7 +124,7 @@
 block discarded – undo
124 124
     public function getIncomingForeignKeys($tableName)
125 125
     {
126 126
         $junctionTables = $this->schemaAnalyzer->detectJunctionTables(true);
127
-        $junctionTableNames = array_map(function (Table $table) {
127
+        $junctionTableNames = array_map(function(Table $table) {
128 128
             return $table->getName();
129 129
         }, $junctionTables);
130 130
         $childrenRelationships = $this->schemaAnalyzer->getChildrenRelationships($tableName);
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/TDBMObject.php 1 patch
Indentation   +34 added lines, -34 removed lines patch added patch discarded remove patch
@@ -33,42 +33,42 @@
 block discarded – undo
33 33
  */
34 34
 class TDBMObject extends AbstractTDBMObject
35 35
 {
36
-    public function getProperty($var, $tableName = null)
37
-    {
38
-        return $this->get($var, $tableName);
39
-    }
36
+	public function getProperty($var, $tableName = null)
37
+	{
38
+		return $this->get($var, $tableName);
39
+	}
40 40
 
41
-    public function setProperty($var, $value, $tableName = null)
42
-    {
43
-        $this->set($var, $value, $tableName);
44
-    }
41
+	public function setProperty($var, $value, $tableName = null)
42
+	{
43
+		$this->set($var, $value, $tableName);
44
+	}
45 45
 
46
-    /**
47
-     * Specify data which should be serialized to JSON.
48
-     *
49
-     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
50
-     *
51
-     * @return mixed data which can be serialized by <b>json_encode</b>,
52
-     *               which is a value of any type other than a resource
53
-     *
54
-     * @since 5.4.0
55
-     */
56
-    public function jsonSerialize()
57
-    {
58
-        throw new TDBMException('Json serialization is only implemented for generated beans.');
59
-    }
46
+	/**
47
+	 * Specify data which should be serialized to JSON.
48
+	 *
49
+	 * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
50
+	 *
51
+	 * @return mixed data which can be serialized by <b>json_encode</b>,
52
+	 *               which is a value of any type other than a resource
53
+	 *
54
+	 * @since 5.4.0
55
+	 */
56
+	public function jsonSerialize()
57
+	{
58
+		throw new TDBMException('Json serialization is only implemented for generated beans.');
59
+	}
60 60
 
61
-    /**
62
-     * Returns an array of used tables by this bean (from parent to child relationship).
63
-     *
64
-     * @return string[]
65
-     */
66
-    protected function getUsedTables()
67
-    {
68
-        $tableNames = array_keys($this->dbRows);
69
-        $tableNames = $this->tdbmService->_getLinkBetweenInheritedTables($tableNames);
70
-        $tableNames = array_reverse($tableNames);
61
+	/**
62
+	 * Returns an array of used tables by this bean (from parent to child relationship).
63
+	 *
64
+	 * @return string[]
65
+	 */
66
+	protected function getUsedTables()
67
+	{
68
+		$tableNames = array_keys($this->dbRows);
69
+		$tableNames = $this->tdbmService->_getLinkBetweenInheritedTables($tableNames);
70
+		$tableNames = array_reverse($tableNames);
71 71
 
72
-        return $tableNames;
73
-    }
72
+		return $tableNames;
73
+	}
74 74
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/InnerResultIterator.php 1 patch
Indentation   +263 added lines, -263 removed lines patch added patch discarded remove patch
@@ -29,267 +29,267 @@
 block discarded – undo
29 29
  */
30 30
 class InnerResultIterator implements \Iterator, \Countable, \ArrayAccess
31 31
 {
32
-    /**
33
-     * @var Statement
34
-     */
35
-    protected $statement;
36
-
37
-    protected $fetchStarted = false;
38
-    private $objectStorage;
39
-    private $className;
40
-
41
-    private $tdbmService;
42
-    private $magicSql;
43
-    private $parameters;
44
-    private $limit;
45
-    private $offset;
46
-    private $columnDescriptors;
47
-    private $magicQuery;
48
-
49
-    /**
50
-     * The key of the current retrieved object.
51
-     *
52
-     * @var int
53
-     */
54
-    protected $key = -1;
55
-
56
-    protected $current = null;
57
-
58
-    private $databasePlatform;
59
-
60
-    /**
61
-     * @var LoggerInterface
62
-     */
63
-    private $logger;
64
-
65
-    public function __construct($magicSql, array $parameters, $limit, $offset, array $columnDescriptors, $objectStorage, $className, TDBMService $tdbmService, MagicQuery $magicQuery, LoggerInterface $logger)
66
-    {
67
-        $this->magicSql = $magicSql;
68
-        $this->objectStorage = $objectStorage;
69
-        $this->className = $className;
70
-        $this->tdbmService = $tdbmService;
71
-        $this->parameters = $parameters;
72
-        $this->limit = $limit;
73
-        $this->offset = $offset;
74
-        $this->columnDescriptors = $columnDescriptors;
75
-        $this->magicQuery = $magicQuery;
76
-        $this->databasePlatform = $this->tdbmService->getConnection()->getDatabasePlatform();
77
-        $this->logger = $logger;
78
-    }
79
-
80
-    protected function executeQuery()
81
-    {
82
-        $sql = $this->magicQuery->build($this->magicSql, $this->parameters);
83
-        $sql = $this->tdbmService->getConnection()->getDatabasePlatform()->modifyLimitQuery($sql, $this->limit, $this->offset);
84
-
85
-        $this->logger->debug('Running SQL request: '.$sql);
86
-
87
-        $this->statement = $this->tdbmService->getConnection()->executeQuery($sql, $this->parameters);
88
-
89
-        $this->fetchStarted = true;
90
-    }
91
-
92
-    /**
93
-     * Counts found records (this is the number of records fetched, taking into account the LIMIT and OFFSET settings).
94
-     *
95
-     * @return int
96
-     */
97
-    public function count()
98
-    {
99
-        if (!$this->fetchStarted) {
100
-            $this->executeQuery();
101
-        }
102
-
103
-        return $this->statement->rowCount();
104
-    }
105
-
106
-    /**
107
-     * Fetches record at current cursor.
108
-     *
109
-     * @return AbstractTDBMObject|null
110
-     */
111
-    public function current()
112
-    {
113
-        return $this->current;
114
-    }
115
-
116
-    /**
117
-     * Returns the current result's key.
118
-     *
119
-     * @return int
120
-     */
121
-    public function key()
122
-    {
123
-        return $this->key;
124
-    }
125
-
126
-    /**
127
-     * Advances the cursor to the next result.
128
-     * Casts the database result into one (or several) beans.
129
-     */
130
-    public function next()
131
-    {
132
-        $row = $this->statement->fetch(\PDO::FETCH_NUM);
133
-        if ($row) {
134
-
135
-            // array<tablegroup, array<table, array<column, value>>>
136
-            $beansData = [];
137
-            foreach ($row as $i => $value) {
138
-                $columnDescriptor = $this->columnDescriptors[$i];
139
-
140
-                if ($columnDescriptor['tableGroup'] === null) {
141
-                    // A column can have no tableGroup (if it comes from an ORDER BY expression)
142
-                    continue;
143
-                }
144
-
145
-                // Let's cast the value according to its type
146
-                $value = $columnDescriptor['type']->convertToPHPValue($value, $this->databasePlatform);
147
-
148
-                $beansData[$columnDescriptor['tableGroup']][$columnDescriptor['table']][$columnDescriptor['column']] = $value;
149
-            }
150
-
151
-            $reflectionClassCache = [];
152
-            $firstBean = true;
153
-            foreach ($beansData as $beanData) {
154
-
155
-                // Let's find the bean class name associated to the bean.
156
-
157
-                list($actualClassName, $mainBeanTableName, $tablesUsed) = $this->tdbmService->_getClassNameFromBeanData($beanData);
158
-
159
-                if ($this->className !== null) {
160
-                    $actualClassName = $this->className;
161
-                }
162
-
163
-                // Let's filter out the beanData that is not used (because it belongs to a part of the hierarchy that is not fetched:
164
-                foreach ($beanData as $tableName => $descriptors) {
165
-                    if (!in_array($tableName, $tablesUsed)) {
166
-                        unset($beanData[$tableName]);
167
-                    }
168
-                }
169
-
170
-                // Must we create the bean? Let's see in the cache if we have a mapping DbRow?
171
-                // Let's get the first object mapping a row:
172
-                // We do this loop only for the first table
173
-
174
-                $primaryKeys = $this->tdbmService->_getPrimaryKeysFromObjectData($mainBeanTableName, $beanData[$mainBeanTableName]);
175
-                $hash = $this->tdbmService->getObjectHash($primaryKeys);
176
-
177
-                if ($this->objectStorage->has($mainBeanTableName, $hash)) {
178
-                    $dbRow = $this->objectStorage->get($mainBeanTableName, $hash);
179
-                    $bean = $dbRow->getTDBMObject();
180
-                } else {
181
-                    // Let's construct the bean
182
-                    if (!isset($reflectionClassCache[$actualClassName])) {
183
-                        $reflectionClassCache[$actualClassName] = new \ReflectionClass($actualClassName);
184
-                    }
185
-                    // Let's bypass the constructor when creating the bean!
186
-                    $bean = $reflectionClassCache[$actualClassName]->newInstanceWithoutConstructor();
187
-                    $bean->_constructFromData($beanData, $this->tdbmService);
188
-                }
189
-
190
-                // The first bean is the one containing the main table.
191
-                if ($firstBean) {
192
-                    $firstBean = false;
193
-                    $this->current = $bean;
194
-                }
195
-            }
196
-
197
-            ++$this->key;
198
-        } else {
199
-            $this->current = null;
200
-        }
201
-    }
202
-
203
-    /**
204
-     * Moves the cursor to the beginning of the result set.
205
-     */
206
-    public function rewind()
207
-    {
208
-        $this->executeQuery();
209
-        $this->key = -1;
210
-        $this->next();
211
-    }
212
-    /**
213
-     * Checks if the cursor is reading a valid result.
214
-     *
215
-     * @return bool
216
-     */
217
-    public function valid()
218
-    {
219
-        return $this->current !== null;
220
-    }
221
-
222
-    /**
223
-     * Whether a offset exists.
224
-     *
225
-     * @link http://php.net/manual/en/arrayaccess.offsetexists.php
226
-     *
227
-     * @param mixed $offset <p>
228
-     *                      An offset to check for.
229
-     *                      </p>
230
-     *
231
-     * @return bool true on success or false on failure.
232
-     *              </p>
233
-     *              <p>
234
-     *              The return value will be casted to boolean if non-boolean was returned
235
-     *
236
-     * @since 5.0.0
237
-     */
238
-    public function offsetExists($offset)
239
-    {
240
-        throw new TDBMInvalidOperationException('You cannot access this result set via index because it was fetched in CURSOR mode. Use ARRAY_MODE instead.');
241
-    }
242
-
243
-    /**
244
-     * Offset to retrieve.
245
-     *
246
-     * @link http://php.net/manual/en/arrayaccess.offsetget.php
247
-     *
248
-     * @param mixed $offset <p>
249
-     *                      The offset to retrieve.
250
-     *                      </p>
251
-     *
252
-     * @return mixed Can return all value types
253
-     *
254
-     * @since 5.0.0
255
-     */
256
-    public function offsetGet($offset)
257
-    {
258
-        throw new TDBMInvalidOperationException('You cannot access this result set via index because it was fetched in CURSOR mode. Use ARRAY_MODE instead.');
259
-    }
260
-
261
-    /**
262
-     * Offset to set.
263
-     *
264
-     * @link http://php.net/manual/en/arrayaccess.offsetset.php
265
-     *
266
-     * @param mixed $offset <p>
267
-     *                      The offset to assign the value to.
268
-     *                      </p>
269
-     * @param mixed $value  <p>
270
-     *                      The value to set.
271
-     *                      </p>
272
-     *
273
-     * @since 5.0.0
274
-     */
275
-    public function offsetSet($offset, $value)
276
-    {
277
-        throw new TDBMInvalidOperationException('You can set values in a TDBM result set.');
278
-    }
279
-
280
-    /**
281
-     * Offset to unset.
282
-     *
283
-     * @link http://php.net/manual/en/arrayaccess.offsetunset.php
284
-     *
285
-     * @param mixed $offset <p>
286
-     *                      The offset to unset.
287
-     *                      </p>
288
-     *
289
-     * @since 5.0.0
290
-     */
291
-    public function offsetUnset($offset)
292
-    {
293
-        throw new TDBMInvalidOperationException('You can unset values in a TDBM result set.');
294
-    }
32
+	/**
33
+	 * @var Statement
34
+	 */
35
+	protected $statement;
36
+
37
+	protected $fetchStarted = false;
38
+	private $objectStorage;
39
+	private $className;
40
+
41
+	private $tdbmService;
42
+	private $magicSql;
43
+	private $parameters;
44
+	private $limit;
45
+	private $offset;
46
+	private $columnDescriptors;
47
+	private $magicQuery;
48
+
49
+	/**
50
+	 * The key of the current retrieved object.
51
+	 *
52
+	 * @var int
53
+	 */
54
+	protected $key = -1;
55
+
56
+	protected $current = null;
57
+
58
+	private $databasePlatform;
59
+
60
+	/**
61
+	 * @var LoggerInterface
62
+	 */
63
+	private $logger;
64
+
65
+	public function __construct($magicSql, array $parameters, $limit, $offset, array $columnDescriptors, $objectStorage, $className, TDBMService $tdbmService, MagicQuery $magicQuery, LoggerInterface $logger)
66
+	{
67
+		$this->magicSql = $magicSql;
68
+		$this->objectStorage = $objectStorage;
69
+		$this->className = $className;
70
+		$this->tdbmService = $tdbmService;
71
+		$this->parameters = $parameters;
72
+		$this->limit = $limit;
73
+		$this->offset = $offset;
74
+		$this->columnDescriptors = $columnDescriptors;
75
+		$this->magicQuery = $magicQuery;
76
+		$this->databasePlatform = $this->tdbmService->getConnection()->getDatabasePlatform();
77
+		$this->logger = $logger;
78
+	}
79
+
80
+	protected function executeQuery()
81
+	{
82
+		$sql = $this->magicQuery->build($this->magicSql, $this->parameters);
83
+		$sql = $this->tdbmService->getConnection()->getDatabasePlatform()->modifyLimitQuery($sql, $this->limit, $this->offset);
84
+
85
+		$this->logger->debug('Running SQL request: '.$sql);
86
+
87
+		$this->statement = $this->tdbmService->getConnection()->executeQuery($sql, $this->parameters);
88
+
89
+		$this->fetchStarted = true;
90
+	}
91
+
92
+	/**
93
+	 * Counts found records (this is the number of records fetched, taking into account the LIMIT and OFFSET settings).
94
+	 *
95
+	 * @return int
96
+	 */
97
+	public function count()
98
+	{
99
+		if (!$this->fetchStarted) {
100
+			$this->executeQuery();
101
+		}
102
+
103
+		return $this->statement->rowCount();
104
+	}
105
+
106
+	/**
107
+	 * Fetches record at current cursor.
108
+	 *
109
+	 * @return AbstractTDBMObject|null
110
+	 */
111
+	public function current()
112
+	{
113
+		return $this->current;
114
+	}
115
+
116
+	/**
117
+	 * Returns the current result's key.
118
+	 *
119
+	 * @return int
120
+	 */
121
+	public function key()
122
+	{
123
+		return $this->key;
124
+	}
125
+
126
+	/**
127
+	 * Advances the cursor to the next result.
128
+	 * Casts the database result into one (or several) beans.
129
+	 */
130
+	public function next()
131
+	{
132
+		$row = $this->statement->fetch(\PDO::FETCH_NUM);
133
+		if ($row) {
134
+
135
+			// array<tablegroup, array<table, array<column, value>>>
136
+			$beansData = [];
137
+			foreach ($row as $i => $value) {
138
+				$columnDescriptor = $this->columnDescriptors[$i];
139
+
140
+				if ($columnDescriptor['tableGroup'] === null) {
141
+					// A column can have no tableGroup (if it comes from an ORDER BY expression)
142
+					continue;
143
+				}
144
+
145
+				// Let's cast the value according to its type
146
+				$value = $columnDescriptor['type']->convertToPHPValue($value, $this->databasePlatform);
147
+
148
+				$beansData[$columnDescriptor['tableGroup']][$columnDescriptor['table']][$columnDescriptor['column']] = $value;
149
+			}
150
+
151
+			$reflectionClassCache = [];
152
+			$firstBean = true;
153
+			foreach ($beansData as $beanData) {
154
+
155
+				// Let's find the bean class name associated to the bean.
156
+
157
+				list($actualClassName, $mainBeanTableName, $tablesUsed) = $this->tdbmService->_getClassNameFromBeanData($beanData);
158
+
159
+				if ($this->className !== null) {
160
+					$actualClassName = $this->className;
161
+				}
162
+
163
+				// Let's filter out the beanData that is not used (because it belongs to a part of the hierarchy that is not fetched:
164
+				foreach ($beanData as $tableName => $descriptors) {
165
+					if (!in_array($tableName, $tablesUsed)) {
166
+						unset($beanData[$tableName]);
167
+					}
168
+				}
169
+
170
+				// Must we create the bean? Let's see in the cache if we have a mapping DbRow?
171
+				// Let's get the first object mapping a row:
172
+				// We do this loop only for the first table
173
+
174
+				$primaryKeys = $this->tdbmService->_getPrimaryKeysFromObjectData($mainBeanTableName, $beanData[$mainBeanTableName]);
175
+				$hash = $this->tdbmService->getObjectHash($primaryKeys);
176
+
177
+				if ($this->objectStorage->has($mainBeanTableName, $hash)) {
178
+					$dbRow = $this->objectStorage->get($mainBeanTableName, $hash);
179
+					$bean = $dbRow->getTDBMObject();
180
+				} else {
181
+					// Let's construct the bean
182
+					if (!isset($reflectionClassCache[$actualClassName])) {
183
+						$reflectionClassCache[$actualClassName] = new \ReflectionClass($actualClassName);
184
+					}
185
+					// Let's bypass the constructor when creating the bean!
186
+					$bean = $reflectionClassCache[$actualClassName]->newInstanceWithoutConstructor();
187
+					$bean->_constructFromData($beanData, $this->tdbmService);
188
+				}
189
+
190
+				// The first bean is the one containing the main table.
191
+				if ($firstBean) {
192
+					$firstBean = false;
193
+					$this->current = $bean;
194
+				}
195
+			}
196
+
197
+			++$this->key;
198
+		} else {
199
+			$this->current = null;
200
+		}
201
+	}
202
+
203
+	/**
204
+	 * Moves the cursor to the beginning of the result set.
205
+	 */
206
+	public function rewind()
207
+	{
208
+		$this->executeQuery();
209
+		$this->key = -1;
210
+		$this->next();
211
+	}
212
+	/**
213
+	 * Checks if the cursor is reading a valid result.
214
+	 *
215
+	 * @return bool
216
+	 */
217
+	public function valid()
218
+	{
219
+		return $this->current !== null;
220
+	}
221
+
222
+	/**
223
+	 * Whether a offset exists.
224
+	 *
225
+	 * @link http://php.net/manual/en/arrayaccess.offsetexists.php
226
+	 *
227
+	 * @param mixed $offset <p>
228
+	 *                      An offset to check for.
229
+	 *                      </p>
230
+	 *
231
+	 * @return bool true on success or false on failure.
232
+	 *              </p>
233
+	 *              <p>
234
+	 *              The return value will be casted to boolean if non-boolean was returned
235
+	 *
236
+	 * @since 5.0.0
237
+	 */
238
+	public function offsetExists($offset)
239
+	{
240
+		throw new TDBMInvalidOperationException('You cannot access this result set via index because it was fetched in CURSOR mode. Use ARRAY_MODE instead.');
241
+	}
242
+
243
+	/**
244
+	 * Offset to retrieve.
245
+	 *
246
+	 * @link http://php.net/manual/en/arrayaccess.offsetget.php
247
+	 *
248
+	 * @param mixed $offset <p>
249
+	 *                      The offset to retrieve.
250
+	 *                      </p>
251
+	 *
252
+	 * @return mixed Can return all value types
253
+	 *
254
+	 * @since 5.0.0
255
+	 */
256
+	public function offsetGet($offset)
257
+	{
258
+		throw new TDBMInvalidOperationException('You cannot access this result set via index because it was fetched in CURSOR mode. Use ARRAY_MODE instead.');
259
+	}
260
+
261
+	/**
262
+	 * Offset to set.
263
+	 *
264
+	 * @link http://php.net/manual/en/arrayaccess.offsetset.php
265
+	 *
266
+	 * @param mixed $offset <p>
267
+	 *                      The offset to assign the value to.
268
+	 *                      </p>
269
+	 * @param mixed $value  <p>
270
+	 *                      The value to set.
271
+	 *                      </p>
272
+	 *
273
+	 * @since 5.0.0
274
+	 */
275
+	public function offsetSet($offset, $value)
276
+	{
277
+		throw new TDBMInvalidOperationException('You can set values in a TDBM result set.');
278
+	}
279
+
280
+	/**
281
+	 * Offset to unset.
282
+	 *
283
+	 * @link http://php.net/manual/en/arrayaccess.offsetunset.php
284
+	 *
285
+	 * @param mixed $offset <p>
286
+	 *                      The offset to unset.
287
+	 *                      </p>
288
+	 *
289
+	 * @since 5.0.0
290
+	 */
291
+	public function offsetUnset($offset)
292
+	{
293
+		throw new TDBMInvalidOperationException('You can unset values in a TDBM result set.');
294
+	}
295 295
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/OrderByAnalyzer.php 2 patches
Indentation   +118 added lines, -118 removed lines patch added patch discarded remove patch
@@ -12,122 +12,122 @@
 block discarded – undo
12 12
  */
13 13
 class OrderByAnalyzer
14 14
 {
15
-    /**
16
-     * The content of the cache variable.
17
-     *
18
-     * @var Cache
19
-     */
20
-    private $cache;
21
-
22
-    /**
23
-     * @var string
24
-     */
25
-    private $cachePrefix;
26
-
27
-    /**
28
-     * OrderByAnalyzer constructor.
29
-     *
30
-     * @param Cache       $cache
31
-     * @param string|null $cachePrefix
32
-     */
33
-    public function __construct(Cache $cache, $cachePrefix = null)
34
-    {
35
-        $this->cache = $cache;
36
-        $this->cachePrefix = $cachePrefix;
37
-    }
38
-
39
-    /**
40
-     * Returns an array for each sorted "column" in the form:.
41
-     *
42
-     * [
43
-     *      [
44
-     *          'type' => 'colref',
45
-     *          'table' => null,
46
-     *          'column' => 'a',
47
-     *          'direction' => 'ASC'
48
-     *      ],
49
-     *      [
50
-     *          'type' => 'expr',
51
-     *          'expr' => 'RAND()',
52
-     *          'direction' => 'DESC'
53
-     *      ]
54
-     * ]
55
-     *
56
-     * @param string $orderBy
57
-     *
58
-     * @return array
59
-     */
60
-    public function analyzeOrderBy(string $orderBy) : array
61
-    {
62
-        $key = $this->cachePrefix.'_order_by_analysis_'.$orderBy;
63
-        $results = $this->cache->fetch($key);
64
-        if ($results !== false) {
65
-            return $results;
66
-        }
67
-        $results = $this->analyzeOrderByNoCache($orderBy);
68
-        $this->cache->save($key, $results);
69
-
70
-        return $results;
71
-    }
72
-
73
-    private function analyzeOrderByNoCache(string $orderBy) : array
74
-    {
75
-        $sqlParser = new PHPSQLParser();
76
-        $sql = 'SELECT 1 FROM a ORDER BY '.$orderBy;
77
-        $parsed = $sqlParser->parse($sql, true);
78
-
79
-        $results = [];
80
-
81
-        for ($i = 0, $count = count($parsed['ORDER']); $i < $count; ++$i) {
82
-            $orderItem = $parsed['ORDER'][$i];
83
-            if ($orderItem['expr_type'] === 'colref') {
84
-                $parts = $orderItem['no_quotes']['parts'];
85
-                $columnName = array_pop($parts);
86
-                if (!empty($parts)) {
87
-                    $tableName = array_pop($parts);
88
-                } else {
89
-                    $tableName = null;
90
-                }
91
-
92
-                $results[] = [
93
-                    'type' => 'colref',
94
-                    'table' => $tableName,
95
-                    'column' => $columnName,
96
-                    'direction' => $orderItem['direction'],
97
-                ];
98
-            } else {
99
-                $position = $orderItem['position'];
100
-                if ($i + 1 < $count) {
101
-                    $nextPosition = $parsed['ORDER'][$i + 1]['position'];
102
-                    $str = substr($sql, $position, $nextPosition - $position);
103
-                } else {
104
-                    $str = substr($sql, $position);
105
-                }
106
-
107
-                $str = trim($str, " \t\r\n,");
108
-
109
-                $results[] = [
110
-                    'type' => 'expr',
111
-                    'expr' => $this->trimDirection($str),
112
-                    'direction' => $orderItem['direction'],
113
-                ];
114
-            }
115
-        }
116
-
117
-        return $results;
118
-    }
119
-
120
-    /**
121
-     * Trims the ASC/DESC direction at the end of the string.
122
-     *
123
-     * @param string $sql
124
-     *
125
-     * @return string
126
-     */
127
-    private function trimDirection(string $sql) : string
128
-    {
129
-        preg_match('/^(.*)(\s+(DESC|ASC|))*$/Ui', $sql, $matches);
130
-
131
-        return $matches[1];
132
-    }
15
+	/**
16
+	 * The content of the cache variable.
17
+	 *
18
+	 * @var Cache
19
+	 */
20
+	private $cache;
21
+
22
+	/**
23
+	 * @var string
24
+	 */
25
+	private $cachePrefix;
26
+
27
+	/**
28
+	 * OrderByAnalyzer constructor.
29
+	 *
30
+	 * @param Cache       $cache
31
+	 * @param string|null $cachePrefix
32
+	 */
33
+	public function __construct(Cache $cache, $cachePrefix = null)
34
+	{
35
+		$this->cache = $cache;
36
+		$this->cachePrefix = $cachePrefix;
37
+	}
38
+
39
+	/**
40
+	 * Returns an array for each sorted "column" in the form:.
41
+	 *
42
+	 * [
43
+	 *      [
44
+	 *          'type' => 'colref',
45
+	 *          'table' => null,
46
+	 *          'column' => 'a',
47
+	 *          'direction' => 'ASC'
48
+	 *      ],
49
+	 *      [
50
+	 *          'type' => 'expr',
51
+	 *          'expr' => 'RAND()',
52
+	 *          'direction' => 'DESC'
53
+	 *      ]
54
+	 * ]
55
+	 *
56
+	 * @param string $orderBy
57
+	 *
58
+	 * @return array
59
+	 */
60
+	public function analyzeOrderBy(string $orderBy) : array
61
+	{
62
+		$key = $this->cachePrefix.'_order_by_analysis_'.$orderBy;
63
+		$results = $this->cache->fetch($key);
64
+		if ($results !== false) {
65
+			return $results;
66
+		}
67
+		$results = $this->analyzeOrderByNoCache($orderBy);
68
+		$this->cache->save($key, $results);
69
+
70
+		return $results;
71
+	}
72
+
73
+	private function analyzeOrderByNoCache(string $orderBy) : array
74
+	{
75
+		$sqlParser = new PHPSQLParser();
76
+		$sql = 'SELECT 1 FROM a ORDER BY '.$orderBy;
77
+		$parsed = $sqlParser->parse($sql, true);
78
+
79
+		$results = [];
80
+
81
+		for ($i = 0, $count = count($parsed['ORDER']); $i < $count; ++$i) {
82
+			$orderItem = $parsed['ORDER'][$i];
83
+			if ($orderItem['expr_type'] === 'colref') {
84
+				$parts = $orderItem['no_quotes']['parts'];
85
+				$columnName = array_pop($parts);
86
+				if (!empty($parts)) {
87
+					$tableName = array_pop($parts);
88
+				} else {
89
+					$tableName = null;
90
+				}
91
+
92
+				$results[] = [
93
+					'type' => 'colref',
94
+					'table' => $tableName,
95
+					'column' => $columnName,
96
+					'direction' => $orderItem['direction'],
97
+				];
98
+			} else {
99
+				$position = $orderItem['position'];
100
+				if ($i + 1 < $count) {
101
+					$nextPosition = $parsed['ORDER'][$i + 1]['position'];
102
+					$str = substr($sql, $position, $nextPosition - $position);
103
+				} else {
104
+					$str = substr($sql, $position);
105
+				}
106
+
107
+				$str = trim($str, " \t\r\n,");
108
+
109
+				$results[] = [
110
+					'type' => 'expr',
111
+					'expr' => $this->trimDirection($str),
112
+					'direction' => $orderItem['direction'],
113
+				];
114
+			}
115
+		}
116
+
117
+		return $results;
118
+	}
119
+
120
+	/**
121
+	 * Trims the ASC/DESC direction at the end of the string.
122
+	 *
123
+	 * @param string $sql
124
+	 *
125
+	 * @return string
126
+	 */
127
+	private function trimDirection(string $sql) : string
128
+	{
129
+		preg_match('/^(.*)(\s+(DESC|ASC|))*$/Ui', $sql, $matches);
130
+
131
+		return $matches[1];
132
+	}
133 133
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -78,7 +78,7 @@  discard block
 block discarded – undo
78 78
 
79 79
         $results = [];
80 80
 
81
-        for ($i = 0, $count = count($parsed['ORDER']); $i < $count; ++$i) {
81
+        for ($i = 0, $count = count($parsed['ORDER']); $i<$count; ++$i) {
82 82
             $orderItem = $parsed['ORDER'][$i];
83 83
             if ($orderItem['expr_type'] === 'colref') {
84 84
                 $parts = $orderItem['no_quotes']['parts'];
@@ -97,9 +97,9 @@  discard block
 block discarded – undo
97 97
                 ];
98 98
             } else {
99 99
                 $position = $orderItem['position'];
100
-                if ($i + 1 < $count) {
101
-                    $nextPosition = $parsed['ORDER'][$i + 1]['position'];
102
-                    $str = substr($sql, $position, $nextPosition - $position);
100
+                if ($i+1<$count) {
101
+                    $nextPosition = $parsed['ORDER'][$i+1]['position'];
102
+                    $str = substr($sql, $position, $nextPosition-$position);
103 103
                 } else {
104 104
                     $str = substr($sql, $position);
105 105
                 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/UncheckedOrderBy.php 1 patch
Indentation   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -20,24 +20,24 @@
 block discarded – undo
20 20
  */
21 21
 class UncheckedOrderBy
22 22
 {
23
-    /**
24
-     * @var string
25
-     */
26
-    private $orderBy;
23
+	/**
24
+	 * @var string
25
+	 */
26
+	private $orderBy;
27 27
 
28
-    /**
29
-     * @param $orderBy
30
-     */
31
-    public function __construct(string $orderBy)
32
-    {
33
-        $this->orderBy = $orderBy;
34
-    }
28
+	/**
29
+	 * @param $orderBy
30
+	 */
31
+	public function __construct(string $orderBy)
32
+	{
33
+		$this->orderBy = $orderBy;
34
+	}
35 35
 
36
-    /**
37
-     * @return string
38
-     */
39
-    public function getOrderBy() : string
40
-    {
41
-        return $this->orderBy;
42
-    }
36
+	/**
37
+	 * @return string
38
+	 */
39
+	public function getOrderBy() : string
40
+	{
41
+		return $this->orderBy;
42
+	}
43 43
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/QueryFactory/FindObjectsQueryFactory.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -39,7 +39,7 @@
 block discarded – undo
39 39
         $sql = 'SELECT DISTINCT '.implode(', ', $columnsList).' FROM MAGICJOIN('.$this->mainTable.')';
40 40
 
41 41
         $pkColumnNames = $this->schema->getTable($this->mainTable)->getPrimaryKeyColumns();
42
-        $pkColumnNames = array_map(function ($pkColumn) {
42
+        $pkColumnNames = array_map(function($pkColumn) {
43 43
             return $this->tdbmService->getConnection()->quoteIdentifier($this->mainTable).'.'.$this->tdbmService->getConnection()->quoteIdentifier($pkColumn);
44 44
         }, $pkColumnNames);
45 45
 
Please login to merge, or discard this patch.
Indentation   +38 added lines, -38 removed lines patch added patch discarded remove patch
@@ -11,44 +11,44 @@
 block discarded – undo
11 11
  */
12 12
 class FindObjectsQueryFactory extends AbstractQueryFactory
13 13
 {
14
-    private $mainTable;
15
-    private $additionalTablesFetch;
16
-    private $filterString;
17
-
18
-    public function __construct(string $mainTable, array $additionalTablesFetch, $filterString, $orderBy, TDBMService $tdbmService, Schema $schema, OrderByAnalyzer $orderByAnalyzer)
19
-    {
20
-        parent::__construct($tdbmService, $schema, $orderByAnalyzer, $orderBy);
21
-        $this->mainTable = $mainTable;
22
-        $this->additionalTablesFetch = $additionalTablesFetch;
23
-        $this->filterString = $filterString;
24
-    }
25
-
26
-    protected function compute()
27
-    {
28
-        list($columnDescList, $columnsList, $orderString) = $this->getColumnsList($this->mainTable, $this->additionalTablesFetch, $this->orderBy);
29
-
30
-        $sql = 'SELECT DISTINCT '.implode(', ', $columnsList).' FROM MAGICJOIN('.$this->mainTable.')';
31
-
32
-        $pkColumnNames = $this->schema->getTable($this->mainTable)->getPrimaryKeyColumns();
33
-        $pkColumnNames = array_map(function ($pkColumn) {
34
-            return $this->tdbmService->getConnection()->quoteIdentifier($this->mainTable).'.'.$this->tdbmService->getConnection()->quoteIdentifier($pkColumn);
35
-        }, $pkColumnNames);
36
-
37
-        $countSql = 'SELECT COUNT(DISTINCT '.implode(', ', $pkColumnNames).') FROM MAGICJOIN('.$this->mainTable.')';
38
-
39
-        if (!empty($this->filterString)) {
40
-            $sql .= ' WHERE '.$this->filterString;
41
-            $countSql .= ' WHERE '.$this->filterString;
42
-        }
43
-
44
-        if (!empty($orderString)) {
45
-            $sql .= ' ORDER BY '.$orderString;
46
-        }
47
-
48
-        $this->magicSql = $sql;
49
-        $this->magicSqlCount = $countSql;
50
-        $this->columnDescList = $columnDescList;
51
-    }
14
+	private $mainTable;
15
+	private $additionalTablesFetch;
16
+	private $filterString;
17
+
18
+	public function __construct(string $mainTable, array $additionalTablesFetch, $filterString, $orderBy, TDBMService $tdbmService, Schema $schema, OrderByAnalyzer $orderByAnalyzer)
19
+	{
20
+		parent::__construct($tdbmService, $schema, $orderByAnalyzer, $orderBy);
21
+		$this->mainTable = $mainTable;
22
+		$this->additionalTablesFetch = $additionalTablesFetch;
23
+		$this->filterString = $filterString;
24
+	}
25
+
26
+	protected function compute()
27
+	{
28
+		list($columnDescList, $columnsList, $orderString) = $this->getColumnsList($this->mainTable, $this->additionalTablesFetch, $this->orderBy);
29
+
30
+		$sql = 'SELECT DISTINCT '.implode(', ', $columnsList).' FROM MAGICJOIN('.$this->mainTable.')';
31
+
32
+		$pkColumnNames = $this->schema->getTable($this->mainTable)->getPrimaryKeyColumns();
33
+		$pkColumnNames = array_map(function ($pkColumn) {
34
+			return $this->tdbmService->getConnection()->quoteIdentifier($this->mainTable).'.'.$this->tdbmService->getConnection()->quoteIdentifier($pkColumn);
35
+		}, $pkColumnNames);
36
+
37
+		$countSql = 'SELECT COUNT(DISTINCT '.implode(', ', $pkColumnNames).') FROM MAGICJOIN('.$this->mainTable.')';
38
+
39
+		if (!empty($this->filterString)) {
40
+			$sql .= ' WHERE '.$this->filterString;
41
+			$countSql .= ' WHERE '.$this->filterString;
42
+		}
43
+
44
+		if (!empty($orderString)) {
45
+			$sql .= ' ORDER BY '.$orderString;
46
+		}
47
+
48
+		$this->magicSql = $sql;
49
+		$this->magicSqlCount = $countSql;
50
+		$this->columnDescList = $columnDescList;
51
+	}
52 52
 
53 53
 
54 54
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/QueryFactory/FindObjectsFromSqlQueryFactory.php 2 patches
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -63,17 +63,17 @@  discard block
 block discarded – undo
63 63
             ];
64 64
         }
65 65
 
66
-        $sql = 'SELECT DISTINCT '.implode(', ', array_map(function ($columnDesc) {
66
+        $sql = 'SELECT DISTINCT '.implode(', ', array_map(function($columnDesc) {
67 67
                 return $this->tdbmService->getConnection()->quoteIdentifier($this->mainTable).'.'.$this->tdbmService->getConnection()->quoteIdentifier($columnDesc['column']);
68 68
             }, $columnDescList)).' FROM '.$this->from;
69 69
 
70
-        if (count($allFetchedTables) > 1) {
70
+        if (count($allFetchedTables)>1) {
71 71
             list($columnDescList, $columnsList, $orderString) = $this->getColumnsList($this->mainTable, [], $this->orderBy);
72 72
         }
73 73
 
74 74
         // Let's compute the COUNT.
75 75
         $pkColumnNames = $this->schema->getTable($this->mainTable)->getPrimaryKeyColumns();
76
-        $pkColumnNames = array_map(function ($pkColumn) {
76
+        $pkColumnNames = array_map(function($pkColumn) {
77 77
             return $this->tdbmService->getConnection()->quoteIdentifier($this->mainTable).'.'.$this->tdbmService->getConnection()->quoteIdentifier($pkColumn);
78 78
         }, $pkColumnNames);
79 79
 
@@ -131,7 +131,7 @@  discard block
 block discarded – undo
131 131
      */
132 132
     private function getParentRelationshipForeignKeys($tableName)
133 133
     {
134
-        return $this->fromCache($this->cachePrefix.'_parentrelationshipfks_'.$tableName, function () use ($tableName) {
134
+        return $this->fromCache($this->cachePrefix.'_parentrelationshipfks_'.$tableName, function() use ($tableName) {
135 135
             return $this->getParentRelationshipForeignKeysWithoutCache($tableName);
136 136
         });
137 137
     }
@@ -160,7 +160,7 @@  discard block
 block discarded – undo
160 160
      */
161 161
     private function getChildrenRelationshipForeignKeys(string $tableName) : array
162 162
     {
163
-        return $this->fromCache($this->cachePrefix.'_childrenrelationshipfks_'.$tableName, function () use ($tableName) {
163
+        return $this->fromCache($this->cachePrefix.'_childrenrelationshipfks_'.$tableName, function() use ($tableName) {
164 164
             return $this->getChildrenRelationshipForeignKeysWithoutCache($tableName);
165 165
         });
166 166
     }
@@ -175,7 +175,7 @@  discard block
 block discarded – undo
175 175
         $children = $this->schemaAnalyzer->getChildrenRelationships($tableName);
176 176
 
177 177
         if (!empty($children)) {
178
-            $fksTables = array_map(function (ForeignKeyConstraint $fk) {
178
+            $fksTables = array_map(function(ForeignKeyConstraint $fk) {
179 179
                 return $this->getChildrenRelationshipForeignKeys($fk->getLocalTableName());
180 180
             }, $children);
181 181
 
Please login to merge, or discard this patch.
Indentation   +182 added lines, -182 removed lines patch added patch discarded remove patch
@@ -15,187 +15,187 @@
 block discarded – undo
15 15
  */
16 16
 class FindObjectsFromSqlQueryFactory extends AbstractQueryFactory
17 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;
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
-        $sql = 'SELECT DISTINCT '.implode(', ', array_map(function ($columnDesc) {
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
-        }
65
-
66
-        // Let's compute the COUNT.
67
-        $pkColumnNames = $this->schema->getTable($this->mainTable)->getPrimaryKeyColumns();
68
-        $pkColumnNames = array_map(function ($pkColumn) {
69
-            return $this->tdbmService->getConnection()->quoteIdentifier($this->mainTable).'.'.$this->tdbmService->getConnection()->quoteIdentifier($pkColumn);
70
-        }, $pkColumnNames);
71
-
72
-        $countSql = 'SELECT COUNT(DISTINCT '.implode(', ', $pkColumnNames).') FROM '.$this->from;
73
-
74
-        if (!empty($this->filterString)) {
75
-            $sql .= ' WHERE '.$this->filterString;
76
-            $countSql .= ' WHERE '.$this->filterString;
77
-        }
78
-
79
-        if (!empty($orderString)) {
80
-            $sql .= ' ORDER BY '.$orderString;
81
-        }
82
-
83
-        if (stripos($countSql, 'GROUP BY') !== false) {
84
-            throw new TDBMException('Unsupported use of GROUP BY in SQL request.');
85
-        }
86
-
87
-        if ($columnsList !== null) {
88
-            $joinSql = '';
89
-            $parentFks = $this->getParentRelationshipForeignKeys($this->mainTable);
90
-            foreach ($parentFks as $fk) {
91
-                $joinSql .= sprintf(' JOIN %s ON (%s.%s = %s.%s)',
92
-                    $connection->quoteIdentifier($fk->getForeignTableName()),
93
-                    $connection->quoteIdentifier($fk->getLocalTableName()),
94
-                    $connection->quoteIdentifier($fk->getLocalColumns()[0]),
95
-                    $connection->quoteIdentifier($fk->getForeignTableName()),
96
-                    $connection->quoteIdentifier($fk->getForeignColumns()[0])
97
-                );
98
-            }
99
-
100
-            $childrenFks = $this->getChildrenRelationshipForeignKeys($this->mainTable);
101
-            foreach ($childrenFks as $fk) {
102
-                $joinSql .= sprintf(' LEFT JOIN %s ON (%s.%s = %s.%s)',
103
-                    $connection->quoteIdentifier($fk->getLocalTableName()),
104
-                    $connection->quoteIdentifier($fk->getForeignTableName()),
105
-                    $connection->quoteIdentifier($fk->getForeignColumns()[0]),
106
-                    $connection->quoteIdentifier($fk->getLocalTableName()),
107
-                    $connection->quoteIdentifier($fk->getLocalColumns()[0])
108
-                );
109
-            }
110
-
111
-            $sql = 'SELECT '.implode(', ', $columnsList).' FROM ('.$sql.') AS '.$this->mainTable.' '.$joinSql;
112
-        }
113
-
114
-        $this->magicSql = $sql;
115
-        $this->magicSqlCount = $countSql;
116
-        $this->columnDescList = $columnDescList;
117
-    }
118
-
119
-    /**
120
-     * @param string $tableName
121
-     *
122
-     * @return ForeignKeyConstraint[]
123
-     */
124
-    private function getParentRelationshipForeignKeys($tableName)
125
-    {
126
-        return $this->fromCache($this->cachePrefix.'_parentrelationshipfks_'.$tableName, function () use ($tableName) {
127
-            return $this->getParentRelationshipForeignKeysWithoutCache($tableName);
128
-        });
129
-    }
130
-
131
-    /**
132
-     * @param string $tableName
133
-     *
134
-     * @return ForeignKeyConstraint[]
135
-     */
136
-    private function getParentRelationshipForeignKeysWithoutCache($tableName)
137
-    {
138
-        $parentFks = [];
139
-        $currentTable = $tableName;
140
-        while ($currentFk = $this->schemaAnalyzer->getParentRelationship($currentTable)) {
141
-            $currentTable = $currentFk->getForeignTableName();
142
-            $parentFks[] = $currentFk;
143
-        }
144
-
145
-        return $parentFks;
146
-    }
147
-
148
-    /**
149
-     * @param string $tableName
150
-     *
151
-     * @return ForeignKeyConstraint[]
152
-     */
153
-    private function getChildrenRelationshipForeignKeys(string $tableName) : array
154
-    {
155
-        return $this->fromCache($this->cachePrefix.'_childrenrelationshipfks_'.$tableName, function () use ($tableName) {
156
-            return $this->getChildrenRelationshipForeignKeysWithoutCache($tableName);
157
-        });
158
-    }
159
-
160
-    /**
161
-     * @param string $tableName
162
-     *
163
-     * @return ForeignKeyConstraint[]
164
-     */
165
-    private function getChildrenRelationshipForeignKeysWithoutCache(string $tableName) : array
166
-    {
167
-        $children = $this->schemaAnalyzer->getChildrenRelationships($tableName);
168
-
169
-        if (!empty($children)) {
170
-            $fksTables = array_map(function (ForeignKeyConstraint $fk) {
171
-                return $this->getChildrenRelationshipForeignKeys($fk->getLocalTableName());
172
-            }, $children);
173
-
174
-            $fks = array_merge($children, call_user_func_array('array_merge', $fksTables));
175
-
176
-            return $fks;
177
-        } else {
178
-            return [];
179
-        }
180
-    }
181
-
182
-    /**
183
-     * Returns an item from cache or computes it using $closure and puts it in cache.
184
-     *
185
-     * @param string   $key
186
-     * @param callable $closure
187
-     *
188
-     * @return mixed
189
-     */
190
-    protected function fromCache(string $key, callable $closure)
191
-    {
192
-        $item = $this->cache->fetch($key);
193
-        if ($item === false) {
194
-            $item = $closure();
195
-            $this->cache->save($key, $item);
196
-        }
197
-
198
-        return $item;
199
-    }
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;
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
+		$sql = 'SELECT DISTINCT '.implode(', ', array_map(function ($columnDesc) {
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
+		}
65
+
66
+		// Let's compute the COUNT.
67
+		$pkColumnNames = $this->schema->getTable($this->mainTable)->getPrimaryKeyColumns();
68
+		$pkColumnNames = array_map(function ($pkColumn) {
69
+			return $this->tdbmService->getConnection()->quoteIdentifier($this->mainTable).'.'.$this->tdbmService->getConnection()->quoteIdentifier($pkColumn);
70
+		}, $pkColumnNames);
71
+
72
+		$countSql = 'SELECT COUNT(DISTINCT '.implode(', ', $pkColumnNames).') FROM '.$this->from;
73
+
74
+		if (!empty($this->filterString)) {
75
+			$sql .= ' WHERE '.$this->filterString;
76
+			$countSql .= ' WHERE '.$this->filterString;
77
+		}
78
+
79
+		if (!empty($orderString)) {
80
+			$sql .= ' ORDER BY '.$orderString;
81
+		}
82
+
83
+		if (stripos($countSql, 'GROUP BY') !== false) {
84
+			throw new TDBMException('Unsupported use of GROUP BY in SQL request.');
85
+		}
86
+
87
+		if ($columnsList !== null) {
88
+			$joinSql = '';
89
+			$parentFks = $this->getParentRelationshipForeignKeys($this->mainTable);
90
+			foreach ($parentFks as $fk) {
91
+				$joinSql .= sprintf(' JOIN %s ON (%s.%s = %s.%s)',
92
+					$connection->quoteIdentifier($fk->getForeignTableName()),
93
+					$connection->quoteIdentifier($fk->getLocalTableName()),
94
+					$connection->quoteIdentifier($fk->getLocalColumns()[0]),
95
+					$connection->quoteIdentifier($fk->getForeignTableName()),
96
+					$connection->quoteIdentifier($fk->getForeignColumns()[0])
97
+				);
98
+			}
99
+
100
+			$childrenFks = $this->getChildrenRelationshipForeignKeys($this->mainTable);
101
+			foreach ($childrenFks as $fk) {
102
+				$joinSql .= sprintf(' LEFT JOIN %s ON (%s.%s = %s.%s)',
103
+					$connection->quoteIdentifier($fk->getLocalTableName()),
104
+					$connection->quoteIdentifier($fk->getForeignTableName()),
105
+					$connection->quoteIdentifier($fk->getForeignColumns()[0]),
106
+					$connection->quoteIdentifier($fk->getLocalTableName()),
107
+					$connection->quoteIdentifier($fk->getLocalColumns()[0])
108
+				);
109
+			}
110
+
111
+			$sql = 'SELECT '.implode(', ', $columnsList).' FROM ('.$sql.') AS '.$this->mainTable.' '.$joinSql;
112
+		}
113
+
114
+		$this->magicSql = $sql;
115
+		$this->magicSqlCount = $countSql;
116
+		$this->columnDescList = $columnDescList;
117
+	}
118
+
119
+	/**
120
+	 * @param string $tableName
121
+	 *
122
+	 * @return ForeignKeyConstraint[]
123
+	 */
124
+	private function getParentRelationshipForeignKeys($tableName)
125
+	{
126
+		return $this->fromCache($this->cachePrefix.'_parentrelationshipfks_'.$tableName, function () use ($tableName) {
127
+			return $this->getParentRelationshipForeignKeysWithoutCache($tableName);
128
+		});
129
+	}
130
+
131
+	/**
132
+	 * @param string $tableName
133
+	 *
134
+	 * @return ForeignKeyConstraint[]
135
+	 */
136
+	private function getParentRelationshipForeignKeysWithoutCache($tableName)
137
+	{
138
+		$parentFks = [];
139
+		$currentTable = $tableName;
140
+		while ($currentFk = $this->schemaAnalyzer->getParentRelationship($currentTable)) {
141
+			$currentTable = $currentFk->getForeignTableName();
142
+			$parentFks[] = $currentFk;
143
+		}
144
+
145
+		return $parentFks;
146
+	}
147
+
148
+	/**
149
+	 * @param string $tableName
150
+	 *
151
+	 * @return ForeignKeyConstraint[]
152
+	 */
153
+	private function getChildrenRelationshipForeignKeys(string $tableName) : array
154
+	{
155
+		return $this->fromCache($this->cachePrefix.'_childrenrelationshipfks_'.$tableName, function () use ($tableName) {
156
+			return $this->getChildrenRelationshipForeignKeysWithoutCache($tableName);
157
+		});
158
+	}
159
+
160
+	/**
161
+	 * @param string $tableName
162
+	 *
163
+	 * @return ForeignKeyConstraint[]
164
+	 */
165
+	private function getChildrenRelationshipForeignKeysWithoutCache(string $tableName) : array
166
+	{
167
+		$children = $this->schemaAnalyzer->getChildrenRelationships($tableName);
168
+
169
+		if (!empty($children)) {
170
+			$fksTables = array_map(function (ForeignKeyConstraint $fk) {
171
+				return $this->getChildrenRelationshipForeignKeys($fk->getLocalTableName());
172
+			}, $children);
173
+
174
+			$fks = array_merge($children, call_user_func_array('array_merge', $fksTables));
175
+
176
+			return $fks;
177
+		} else {
178
+			return [];
179
+		}
180
+	}
181
+
182
+	/**
183
+	 * Returns an item from cache or computes it using $closure and puts it in cache.
184
+	 *
185
+	 * @param string   $key
186
+	 * @param callable $closure
187
+	 *
188
+	 * @return mixed
189
+	 */
190
+	protected function fromCache(string $key, callable $closure)
191
+	{
192
+		$item = $this->cache->fetch($key);
193
+		if ($item === false) {
194
+			$item = $closure();
195
+			$this->cache->save($key, $item);
196
+		}
197
+
198
+		return $item;
199
+	}
200 200
 
201 201
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/QueryFactory/AbstractQueryFactory.php 1 patch
Indentation   +203 added lines, -203 removed lines patch added patch discarded remove patch
@@ -10,207 +10,207 @@
 block discarded – undo
10 10
 
11 11
 abstract class AbstractQueryFactory implements QueryFactory
12 12
 {
13
-    /**
14
-     * @var TDBMService
15
-     */
16
-    protected $tdbmService;
17
-
18
-    /**
19
-     * @var Schema
20
-     */
21
-    protected $schema;
22
-
23
-    /**
24
-     * @var OrderByAnalyzer
25
-     */
26
-    protected $orderByAnalyzer;
27
-
28
-    /**
29
-     * @var string|UncheckedOrderBy|null
30
-     */
31
-    protected $orderBy;
32
-
33
-    protected $magicSql;
34
-    protected $magicSqlCount;
35
-    protected $columnDescList;
36
-
37
-    /**
38
-     * @param TDBMService $tdbmService
39
-     */
40
-    public function __construct(TDBMService $tdbmService, Schema $schema, OrderByAnalyzer $orderByAnalyzer, $orderBy)
41
-    {
42
-        $this->tdbmService = $tdbmService;
43
-        $this->schema = $schema;
44
-        $this->orderByAnalyzer = $orderByAnalyzer;
45
-        $this->orderBy = $orderBy;
46
-    }
47
-
48
-    /**
49
-     * Returns the column list that must be fetched for the SQL request.
50
-     *
51
-     * Note: MySQL dictates that ORDER BYed columns should appear in the SELECT clause.
52
-     *
53
-     * @param string                       $mainTable
54
-     * @param array                        $additionalTablesFetch
55
-     * @param string|UncheckedOrderBy|null $orderBy
56
-     *
57
-     * @return array
58
-     *
59
-     * @throws \Doctrine\DBAL\Schema\SchemaException
60
-     */
61
-    protected function getColumnsList(string $mainTable, array $additionalTablesFetch = array(), $orderBy = null)
62
-    {
63
-        // From the table name and the additional tables we want to fetch, let's build a list of all tables
64
-        // that must be part of the select columns.
65
-
66
-        $connection = $this->tdbmService->getConnection();
67
-
68
-        $tableGroups = [];
69
-        $allFetchedTables = $this->tdbmService->_getRelatedTablesByInheritance($mainTable);
70
-        $tableGroupName = $this->getTableGroupName($allFetchedTables);
71
-        foreach ($allFetchedTables as $table) {
72
-            $tableGroups[$table] = $tableGroupName;
73
-        }
74
-
75
-        $columnsList = [];
76
-        $columnDescList = [];
77
-        $sortColumn = 0;
78
-        $reconstructedOrderBy = null;
79
-
80
-        // Now, let's deal with "order by columns"
81
-        if ($orderBy !== null) {
82
-            if ($orderBy instanceof UncheckedOrderBy) {
83
-                $securedOrderBy = false;
84
-                $orderBy = $orderBy->getOrderBy();
85
-                $reconstructedOrderBy = $orderBy;
86
-            } else {
87
-                $securedOrderBy = true;
88
-                $reconstructedOrderBys = [];
89
-            }
90
-            $orderByColumns = $this->orderByAnalyzer->analyzeOrderBy($orderBy);
91
-
92
-            // If we sort by a column, there is a high chance we will fetch the bean containing this column.
93
-            // Hence, we should add the table to the $additionalTablesFetch
94
-            foreach ($orderByColumns as $orderByColumn) {
95
-                if ($orderByColumn['type'] === 'colref') {
96
-                    if ($orderByColumn['table'] !== null) {
97
-                        $additionalTablesFetch[] = $orderByColumn['table'];
98
-                    }
99
-                    if ($securedOrderBy) {
100
-                        $reconstructedOrderBys[] = ($orderByColumn['table'] !== null ? $orderByColumn['table'].'.' : '').$orderByColumn['column'].' '.$orderByColumn['direction'];
101
-                    }
102
-                } elseif ($orderByColumn['type'] === 'expr') {
103
-                    $sortColumnName = 'sort_column_'.$sortColumn;
104
-                    $columnsList[] = $orderByColumn['expr'].' as '.$sortColumnName;
105
-                    $columnDescList[] = [
106
-                        'tableGroup' => null,
107
-                    ];
108
-                    ++$sortColumn;
109
-
110
-                    if ($securedOrderBy) {
111
-                        throw new TDBMInvalidArgumentException('Invalid ORDER BY column: "'.$orderByColumn['expr'].'". If you want to use expression in your ORDER BY clause, you must wrap them in a UncheckedOrderBy object. For instance: new UncheckedOrderBy("col1 + col2 DESC")');
112
-                    }
113
-                }
114
-            }
115
-
116
-            if ($reconstructedOrderBy === null) {
117
-                $reconstructedOrderBy = implode(', ', $reconstructedOrderBys);
118
-            }
119
-        }
120
-
121
-        foreach ($additionalTablesFetch as $additionalTable) {
122
-            $relatedTables = $this->tdbmService->_getRelatedTablesByInheritance($additionalTable);
123
-            $tableGroupName = $this->getTableGroupName($relatedTables);
124
-            foreach ($relatedTables as $table) {
125
-                $tableGroups[$table] = $tableGroupName;
126
-            }
127
-            $allFetchedTables = array_merge($allFetchedTables, $relatedTables);
128
-        }
129
-
130
-        // Let's remove any duplicate
131
-        $allFetchedTables = array_flip(array_flip($allFetchedTables));
132
-
133
-        // Now, let's build the column list
134
-        foreach ($allFetchedTables as $table) {
135
-            foreach ($this->schema->getTable($table)->getColumns() as $column) {
136
-                $columnName = $column->getName();
137
-                $columnDescList[] = [
138
-                    'as' => $table.'____'.$columnName,
139
-                    'table' => $table,
140
-                    'column' => $columnName,
141
-                    'type' => $column->getType(),
142
-                    'tableGroup' => $tableGroups[$table],
143
-                ];
144
-                $columnsList[] = $connection->quoteIdentifier($table).'.'.$connection->quoteIdentifier($columnName).' as '.
145
-                    $connection->quoteIdentifier($table.'____'.$columnName);
146
-            }
147
-        }
148
-
149
-        return [$columnDescList, $columnsList, $reconstructedOrderBy];
150
-    }
151
-
152
-    abstract protected function compute();
153
-
154
-    /**
155
-     * Returns an identifier for the group of tables passed in parameter.
156
-     *
157
-     * @param string[] $relatedTables
158
-     *
159
-     * @return string
160
-     */
161
-    protected function getTableGroupName(array $relatedTables)
162
-    {
163
-        sort($relatedTables);
164
-
165
-        return implode('_``_', $relatedTables);
166
-    }
167
-
168
-    public function getMagicSql() : string
169
-    {
170
-        if ($this->magicSql === null) {
171
-            $this->compute();
172
-        }
173
-
174
-        return $this->magicSql;
175
-    }
176
-
177
-    public function getMagicSqlCount() : string
178
-    {
179
-        if ($this->magicSqlCount === null) {
180
-            $this->compute();
181
-        }
182
-
183
-        return $this->magicSqlCount;
184
-    }
185
-
186
-    public function getColumnDescriptors() : array
187
-    {
188
-        if ($this->columnDescList === null) {
189
-            $this->compute();
190
-        }
191
-
192
-        return $this->columnDescList;
193
-    }
194
-
195
-    /**
196
-     * Sets the ORDER BY directive executed in SQL.
197
-     *
198
-     * For instance:
199
-     *
200
-     *  $queryFactory->sort('label ASC, status DESC');
201
-     *
202
-     * **Important:** TDBM does its best to protect you from SQL injection. In particular, it will only allow column names in the "ORDER BY" clause. This means you are safe to pass input from the user directly in the ORDER BY parameter.
203
-     * If you want to pass an expression to the ORDER BY clause, you will need to tell TDBM to stop checking for SQL injections. You do this by passing a `UncheckedOrderBy` object as a parameter:
204
-     *
205
-     *  $queryFactory->sort(new UncheckedOrderBy('RAND()'))
206
-     *
207
-     * @param string|UncheckedOrderBy|null $orderBy
208
-     */
209
-    public function sort($orderBy)
210
-    {
211
-        $this->orderBy = $orderBy;
212
-        $this->magicSql = null;
213
-        $this->magicSqlCount = null;
214
-        $this->columnDescList = null;
215
-    }
13
+	/**
14
+	 * @var TDBMService
15
+	 */
16
+	protected $tdbmService;
17
+
18
+	/**
19
+	 * @var Schema
20
+	 */
21
+	protected $schema;
22
+
23
+	/**
24
+	 * @var OrderByAnalyzer
25
+	 */
26
+	protected $orderByAnalyzer;
27
+
28
+	/**
29
+	 * @var string|UncheckedOrderBy|null
30
+	 */
31
+	protected $orderBy;
32
+
33
+	protected $magicSql;
34
+	protected $magicSqlCount;
35
+	protected $columnDescList;
36
+
37
+	/**
38
+	 * @param TDBMService $tdbmService
39
+	 */
40
+	public function __construct(TDBMService $tdbmService, Schema $schema, OrderByAnalyzer $orderByAnalyzer, $orderBy)
41
+	{
42
+		$this->tdbmService = $tdbmService;
43
+		$this->schema = $schema;
44
+		$this->orderByAnalyzer = $orderByAnalyzer;
45
+		$this->orderBy = $orderBy;
46
+	}
47
+
48
+	/**
49
+	 * Returns the column list that must be fetched for the SQL request.
50
+	 *
51
+	 * Note: MySQL dictates that ORDER BYed columns should appear in the SELECT clause.
52
+	 *
53
+	 * @param string                       $mainTable
54
+	 * @param array                        $additionalTablesFetch
55
+	 * @param string|UncheckedOrderBy|null $orderBy
56
+	 *
57
+	 * @return array
58
+	 *
59
+	 * @throws \Doctrine\DBAL\Schema\SchemaException
60
+	 */
61
+	protected function getColumnsList(string $mainTable, array $additionalTablesFetch = array(), $orderBy = null)
62
+	{
63
+		// From the table name and the additional tables we want to fetch, let's build a list of all tables
64
+		// that must be part of the select columns.
65
+
66
+		$connection = $this->tdbmService->getConnection();
67
+
68
+		$tableGroups = [];
69
+		$allFetchedTables = $this->tdbmService->_getRelatedTablesByInheritance($mainTable);
70
+		$tableGroupName = $this->getTableGroupName($allFetchedTables);
71
+		foreach ($allFetchedTables as $table) {
72
+			$tableGroups[$table] = $tableGroupName;
73
+		}
74
+
75
+		$columnsList = [];
76
+		$columnDescList = [];
77
+		$sortColumn = 0;
78
+		$reconstructedOrderBy = null;
79
+
80
+		// Now, let's deal with "order by columns"
81
+		if ($orderBy !== null) {
82
+			if ($orderBy instanceof UncheckedOrderBy) {
83
+				$securedOrderBy = false;
84
+				$orderBy = $orderBy->getOrderBy();
85
+				$reconstructedOrderBy = $orderBy;
86
+			} else {
87
+				$securedOrderBy = true;
88
+				$reconstructedOrderBys = [];
89
+			}
90
+			$orderByColumns = $this->orderByAnalyzer->analyzeOrderBy($orderBy);
91
+
92
+			// If we sort by a column, there is a high chance we will fetch the bean containing this column.
93
+			// Hence, we should add the table to the $additionalTablesFetch
94
+			foreach ($orderByColumns as $orderByColumn) {
95
+				if ($orderByColumn['type'] === 'colref') {
96
+					if ($orderByColumn['table'] !== null) {
97
+						$additionalTablesFetch[] = $orderByColumn['table'];
98
+					}
99
+					if ($securedOrderBy) {
100
+						$reconstructedOrderBys[] = ($orderByColumn['table'] !== null ? $orderByColumn['table'].'.' : '').$orderByColumn['column'].' '.$orderByColumn['direction'];
101
+					}
102
+				} elseif ($orderByColumn['type'] === 'expr') {
103
+					$sortColumnName = 'sort_column_'.$sortColumn;
104
+					$columnsList[] = $orderByColumn['expr'].' as '.$sortColumnName;
105
+					$columnDescList[] = [
106
+						'tableGroup' => null,
107
+					];
108
+					++$sortColumn;
109
+
110
+					if ($securedOrderBy) {
111
+						throw new TDBMInvalidArgumentException('Invalid ORDER BY column: "'.$orderByColumn['expr'].'". If you want to use expression in your ORDER BY clause, you must wrap them in a UncheckedOrderBy object. For instance: new UncheckedOrderBy("col1 + col2 DESC")');
112
+					}
113
+				}
114
+			}
115
+
116
+			if ($reconstructedOrderBy === null) {
117
+				$reconstructedOrderBy = implode(', ', $reconstructedOrderBys);
118
+			}
119
+		}
120
+
121
+		foreach ($additionalTablesFetch as $additionalTable) {
122
+			$relatedTables = $this->tdbmService->_getRelatedTablesByInheritance($additionalTable);
123
+			$tableGroupName = $this->getTableGroupName($relatedTables);
124
+			foreach ($relatedTables as $table) {
125
+				$tableGroups[$table] = $tableGroupName;
126
+			}
127
+			$allFetchedTables = array_merge($allFetchedTables, $relatedTables);
128
+		}
129
+
130
+		// Let's remove any duplicate
131
+		$allFetchedTables = array_flip(array_flip($allFetchedTables));
132
+
133
+		// Now, let's build the column list
134
+		foreach ($allFetchedTables as $table) {
135
+			foreach ($this->schema->getTable($table)->getColumns() as $column) {
136
+				$columnName = $column->getName();
137
+				$columnDescList[] = [
138
+					'as' => $table.'____'.$columnName,
139
+					'table' => $table,
140
+					'column' => $columnName,
141
+					'type' => $column->getType(),
142
+					'tableGroup' => $tableGroups[$table],
143
+				];
144
+				$columnsList[] = $connection->quoteIdentifier($table).'.'.$connection->quoteIdentifier($columnName).' as '.
145
+					$connection->quoteIdentifier($table.'____'.$columnName);
146
+			}
147
+		}
148
+
149
+		return [$columnDescList, $columnsList, $reconstructedOrderBy];
150
+	}
151
+
152
+	abstract protected function compute();
153
+
154
+	/**
155
+	 * Returns an identifier for the group of tables passed in parameter.
156
+	 *
157
+	 * @param string[] $relatedTables
158
+	 *
159
+	 * @return string
160
+	 */
161
+	protected function getTableGroupName(array $relatedTables)
162
+	{
163
+		sort($relatedTables);
164
+
165
+		return implode('_``_', $relatedTables);
166
+	}
167
+
168
+	public function getMagicSql() : string
169
+	{
170
+		if ($this->magicSql === null) {
171
+			$this->compute();
172
+		}
173
+
174
+		return $this->magicSql;
175
+	}
176
+
177
+	public function getMagicSqlCount() : string
178
+	{
179
+		if ($this->magicSqlCount === null) {
180
+			$this->compute();
181
+		}
182
+
183
+		return $this->magicSqlCount;
184
+	}
185
+
186
+	public function getColumnDescriptors() : array
187
+	{
188
+		if ($this->columnDescList === null) {
189
+			$this->compute();
190
+		}
191
+
192
+		return $this->columnDescList;
193
+	}
194
+
195
+	/**
196
+	 * Sets the ORDER BY directive executed in SQL.
197
+	 *
198
+	 * For instance:
199
+	 *
200
+	 *  $queryFactory->sort('label ASC, status DESC');
201
+	 *
202
+	 * **Important:** TDBM does its best to protect you from SQL injection. In particular, it will only allow column names in the "ORDER BY" clause. This means you are safe to pass input from the user directly in the ORDER BY parameter.
203
+	 * If you want to pass an expression to the ORDER BY clause, you will need to tell TDBM to stop checking for SQL injections. You do this by passing a `UncheckedOrderBy` object as a parameter:
204
+	 *
205
+	 *  $queryFactory->sort(new UncheckedOrderBy('RAND()'))
206
+	 *
207
+	 * @param string|UncheckedOrderBy|null $orderBy
208
+	 */
209
+	public function sort($orderBy)
210
+	{
211
+		$this->orderBy = $orderBy;
212
+		$this->magicSql = null;
213
+		$this->magicSqlCount = null;
214
+		$this->columnDescList = null;
215
+	}
216 216
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/QueryFactory/QueryFactory.php 1 patch
Indentation   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -9,25 +9,25 @@
 block discarded – undo
9 9
  */
10 10
 interface QueryFactory
11 11
 {
12
-    /**
13
-     * Sets the ORDER BY directive executed in SQL.
14
-     *
15
-     * For instance:
16
-     *
17
-     *  $queryFactory->sort('label ASC, status DESC');
18
-     *
19
-     * **Important:** TDBM does its best to protect you from SQL injection. In particular, it will only allow column names in the "ORDER BY" clause. This means you are safe to pass input from the user directly in the ORDER BY parameter.
20
-     * If you want to pass an expression to the ORDER BY clause, you will need to tell TDBM to stop checking for SQL injections. You do this by passing a `UncheckedOrderBy` object as a parameter:
21
-     *
22
-     *  $queryFactory->sort(new UncheckedOrderBy('RAND()'))
23
-     *
24
-     * @param string|UncheckedOrderBy|null $orderBy
25
-     */
26
-    public function sort($orderBy);
12
+	/**
13
+	 * Sets the ORDER BY directive executed in SQL.
14
+	 *
15
+	 * For instance:
16
+	 *
17
+	 *  $queryFactory->sort('label ASC, status DESC');
18
+	 *
19
+	 * **Important:** TDBM does its best to protect you from SQL injection. In particular, it will only allow column names in the "ORDER BY" clause. This means you are safe to pass input from the user directly in the ORDER BY parameter.
20
+	 * If you want to pass an expression to the ORDER BY clause, you will need to tell TDBM to stop checking for SQL injections. You do this by passing a `UncheckedOrderBy` object as a parameter:
21
+	 *
22
+	 *  $queryFactory->sort(new UncheckedOrderBy('RAND()'))
23
+	 *
24
+	 * @param string|UncheckedOrderBy|null $orderBy
25
+	 */
26
+	public function sort($orderBy);
27 27
 
28
-    public function getMagicSql() : string;
28
+	public function getMagicSql() : string;
29 29
 
30
-    public function getMagicSqlCount() : string;
30
+	public function getMagicSqlCount() : string;
31 31
 
32
-    public function getColumnDescriptors() : array;
32
+	public function getColumnDescriptors() : array;
33 33
 }
Please login to merge, or discard this patch.