Passed
Push — master ( feefe7...5f3c58 )
by David
03:30
created

InnerResultIterator::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 12
nc 1
nop 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace TheCodingMachine\TDBM;
4
5
use Doctrine\DBAL\Platforms\MySqlPlatform;
6
use Doctrine\DBAL\Statement;
7
use Mouf\Database\MagicQuery;
8
use Psr\Log\LoggerInterface;
9
10
/*
11
 Copyright (C) 2006-2017 David Négrier - THE CODING MACHINE
12
13
 This program is free software; you can redistribute it and/or modify
14
 it under the terms of the GNU General Public License as published by
15
 the Free Software Foundation; either version 2 of the License, or
16
 (at your option) any later version.
17
18
 This program is distributed in the hope that it will be useful,
19
 but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 GNU General Public License for more details.
22
23
 You should have received a copy of the GNU General Public License
24
 along with this program; if not, write to the Free Software
25
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
26
 */
27
28
/**
29
 * Iterator used to retrieve results.
30
 */
31
class InnerResultIterator implements \Iterator, \Countable, \ArrayAccess
32
{
33
    /**
34
     * @var Statement
35
     */
36
    protected $statement;
37
38
    protected $fetchStarted = false;
39
    private $objectStorage;
40
    private $className;
41
42
    private $tdbmService;
43
    private $magicSql;
44
    private $parameters;
45
    private $limit;
46
    private $offset;
47
    private $columnDescriptors;
48
    private $magicQuery;
49
50
    /**
51
     * The key of the current retrieved object.
52
     *
53
     * @var int
54
     */
55
    protected $key = -1;
56
57
    protected $current = null;
58
59
    private $databasePlatform;
60
61
    /**
62
     * @var LoggerInterface
63
     */
64
    private $logger;
65
66
    public function __construct($magicSql, array $parameters, $limit, $offset, array $columnDescriptors, $objectStorage, $className, TDBMService $tdbmService, MagicQuery $magicQuery, LoggerInterface $logger)
67
    {
68
        $this->magicSql = $magicSql;
69
        $this->objectStorage = $objectStorage;
70
        $this->className = $className;
71
        $this->tdbmService = $tdbmService;
72
        $this->parameters = $parameters;
73
        $this->limit = $limit;
74
        $this->offset = $offset;
75
        $this->columnDescriptors = $columnDescriptors;
76
        $this->magicQuery = $magicQuery;
77
        $this->databasePlatform = $this->tdbmService->getConnection()->getDatabasePlatform();
78
        $this->logger = $logger;
79
    }
80
81
    private function getQuery(): string
82
    {
83
        $sql = $this->magicQuery->build($this->magicSql, $this->parameters);
84
        $sql = $this->tdbmService->getConnection()->getDatabasePlatform()->modifyLimitQuery($sql, $this->limit, $this->offset);
85
        return $sql;
86
    }
87
88
    protected function executeQuery()
89
    {
90
        $sql = $this->getQuery();
91
92
        $this->logger->debug('Running SQL request: '.$sql);
93
94
        $this->statement = $this->tdbmService->getConnection()->executeQuery($sql, $this->parameters);
95
96
        $this->fetchStarted = true;
97
    }
98
99
    /**
100
     * Counts found records (this is the number of records fetched, taking into account the LIMIT and OFFSET settings).
101
     *
102
     * @return int
103
     */
104
    public function count()
105
    {
106
        if ($this->count !== null) {
107
            return $this->count;
108
        }
109
110
        if ($this->tdbmService->getConnection()->getDatabasePlatform() instanceof MySqlPlatform) {
111
            // Optimisation: we don't need a separate "count" SQL request in MySQL.
112
            return $this->getRowCountViaRowCountFunction();
113
        } else {
114
            return $this->getRowCountViaSqlQuery();
115
        }
116
    }
117
118
    private $count = null;
119
120
    /**
121
     * Get the row count from the rowCount function (only works with MySQL)
122
     */
123
    private function getRowCountViaRowCountFunction(): int
124
    {
125
        if (!$this->fetchStarted) {
126
            $this->executeQuery();
127
        }
128
129
        $this->count = $this->statement->rowCount();
130
        return $this->count;
131
    }
132
133
134
    /**
135
     * Makes a separate SQL query to compute the row count.
136
     * (not needed in MySQL)
137
     */
138
    private function getRowCountViaSqlQuery(): int
139
    {
140
        $countSql = 'SELECT COUNT(1) FROM ('.$this->getQuery().') c';
141
142
        $this->logger->debug('Running count SQL request: '.$countSql);
143
144
        $this->count = $this->tdbmService->getConnection()->fetchColumn($countSql, $this->parameters);
145
        return $this->count;
146
    }
147
148
    /**
149
     * Fetches record at current cursor.
150
     *
151
     * @return AbstractTDBMObject|null
152
     */
153
    public function current()
154
    {
155
        return $this->current;
156
    }
157
158
    /**
159
     * Returns the current result's key.
160
     *
161
     * @return int
162
     */
163
    public function key()
164
    {
165
        return $this->key;
166
    }
167
168
    /**
169
     * Advances the cursor to the next result.
170
     * Casts the database result into one (or several) beans.
171
     */
172
    public function next()
173
    {
174
        $row = $this->statement->fetch(\PDO::FETCH_NUM);
175
        if ($row) {
176
177
            // array<tablegroup, array<table, array<column, value>>>
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
178
            $beansData = [];
179
            foreach ($row as $i => $value) {
180
                $columnDescriptor = $this->columnDescriptors[$i];
181
182
                if ($columnDescriptor['tableGroup'] === null) {
183
                    // A column can have no tableGroup (if it comes from an ORDER BY expression)
184
                    continue;
185
                }
186
187
                // Let's cast the value according to its type
188
                $value = $columnDescriptor['type']->convertToPHPValue($value, $this->databasePlatform);
189
190
                $beansData[$columnDescriptor['tableGroup']][$columnDescriptor['table']][$columnDescriptor['column']] = $value;
191
            }
192
193
            $reflectionClassCache = [];
194
            $firstBean = true;
195
            foreach ($beansData as $beanData) {
196
197
                // Let's find the bean class name associated to the bean.
198
199
                list($actualClassName, $mainBeanTableName, $tablesUsed) = $this->tdbmService->_getClassNameFromBeanData($beanData);
200
201
                if ($this->className !== null) {
202
                    $actualClassName = $this->className;
203
                }
204
205
                // Let's filter out the beanData that is not used (because it belongs to a part of the hierarchy that is not fetched:
206
                foreach ($beanData as $tableName => $descriptors) {
207
                    if (!in_array($tableName, $tablesUsed)) {
208
                        unset($beanData[$tableName]);
209
                    }
210
                }
211
212
                // Must we create the bean? Let's see in the cache if we have a mapping DbRow?
213
                // Let's get the first object mapping a row:
214
                // We do this loop only for the first table
215
216
                $primaryKeys = $this->tdbmService->_getPrimaryKeysFromObjectData($mainBeanTableName, $beanData[$mainBeanTableName]);
217
                $hash = $this->tdbmService->getObjectHash($primaryKeys);
218
219
                if ($this->objectStorage->has($mainBeanTableName, $hash)) {
220
                    $dbRow = $this->objectStorage->get($mainBeanTableName, $hash);
221
                    $bean = $dbRow->getTDBMObject();
222
                } else {
223
                    // Let's construct the bean
224
                    if (!isset($reflectionClassCache[$actualClassName])) {
225
                        $reflectionClassCache[$actualClassName] = new \ReflectionClass($actualClassName);
226
                    }
227
                    // Let's bypass the constructor when creating the bean!
228
                    $bean = $reflectionClassCache[$actualClassName]->newInstanceWithoutConstructor();
229
                    $bean->_constructFromData($beanData, $this->tdbmService);
230
                }
231
232
                // The first bean is the one containing the main table.
233
                if ($firstBean) {
234
                    $firstBean = false;
235
                    $this->current = $bean;
236
                }
237
            }
238
239
            ++$this->key;
240
        } else {
241
            $this->current = null;
242
        }
243
    }
244
245
    /**
246
     * Moves the cursor to the beginning of the result set.
247
     */
248
    public function rewind()
249
    {
250
        $this->executeQuery();
251
        $this->key = -1;
252
        $this->next();
253
    }
254
    /**
255
     * Checks if the cursor is reading a valid result.
256
     *
257
     * @return bool
258
     */
259
    public function valid()
260
    {
261
        return $this->current !== null;
262
    }
263
264
    /**
265
     * Whether a offset exists.
266
     *
267
     * @link http://php.net/manual/en/arrayaccess.offsetexists.php
268
     *
269
     * @param mixed $offset <p>
270
     *                      An offset to check for.
271
     *                      </p>
272
     *
273
     * @return bool true on success or false on failure.
274
     *              </p>
275
     *              <p>
276
     *              The return value will be casted to boolean if non-boolean was returned
277
     *
278
     * @since 5.0.0
279
     */
280
    public function offsetExists($offset)
281
    {
282
        throw new TDBMInvalidOperationException('You cannot access this result set via index because it was fetched in CURSOR mode. Use ARRAY_MODE instead.');
283
    }
284
285
    /**
286
     * Offset to retrieve.
287
     *
288
     * @link http://php.net/manual/en/arrayaccess.offsetget.php
289
     *
290
     * @param mixed $offset <p>
291
     *                      The offset to retrieve.
292
     *                      </p>
293
     *
294
     * @return mixed Can return all value types
295
     *
296
     * @since 5.0.0
297
     */
298
    public function offsetGet($offset)
299
    {
300
        throw new TDBMInvalidOperationException('You cannot access this result set via index because it was fetched in CURSOR mode. Use ARRAY_MODE instead.');
301
    }
302
303
    /**
304
     * Offset to set.
305
     *
306
     * @link http://php.net/manual/en/arrayaccess.offsetset.php
307
     *
308
     * @param mixed $offset <p>
309
     *                      The offset to assign the value to.
310
     *                      </p>
311
     * @param mixed $value  <p>
312
     *                      The value to set.
313
     *                      </p>
314
     *
315
     * @since 5.0.0
316
     */
317
    public function offsetSet($offset, $value)
318
    {
319
        throw new TDBMInvalidOperationException('You can set values in a TDBM result set.');
320
    }
321
322
    /**
323
     * Offset to unset.
324
     *
325
     * @link http://php.net/manual/en/arrayaccess.offsetunset.php
326
     *
327
     * @param mixed $offset <p>
328
     *                      The offset to unset.
329
     *                      </p>
330
     *
331
     * @since 5.0.0
332
     */
333
    public function offsetUnset($offset)
334
    {
335
        throw new TDBMInvalidOperationException('You can unset values in a TDBM result set.');
336
    }
337
}
338