Completed
Pull Request — master (#9)
by Samuel
03:17
created

Selection::__call()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.3332

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 13
ccs 6
cts 9
cp 0.6667
rs 9.4285
cc 3
eloc 8
nc 3
nop 2
crap 3.3332
1
<?php
2
3
namespace SimpleMapper;
4
5
use Nette\Database\Table\IRow;
6
use Nette\Database\Table\Selection as NetteDatabaseSelection;
7
use Nette\Database\Table\ActiveRow as NetteDatabaseActiveRow;
8
use Nette\InvalidArgumentException;
9
use ArrayAccess;
10
use Iterator;
11
use Countable;
12
use SimpleMapper\Structure\Structure;
13
use Traversable;
14
15
class Selection implements Iterator, Countable, ArrayAccess
16
{
17
    /** @var NetteDatabaseSelection */
18
    private $selection;
19
20
    /** @var Structure */
21
    protected $structure;
22
23
    /**
24
     * @param NetteDatabaseSelection $selection
25
     * @param Structure $structure
26
     */
27 60
    public function __construct(NetteDatabaseSelection $selection, Structure $structure)
28
    {
29 60
        $this->selection = $selection;
30 60
        $this->structure = $structure;
31 60
    }
32
33
    /**
34
     * @return NetteDatabaseSelection
35
     */
36 4
    public function getSelection()
37
    {
38 4
        return $this->selection;
39
    }
40
41
    /**
42
     * Clone object
43
     */
44
    public function __clone()
45
    {
46
        $this->selection = clone $this->selection;
47
    }
48
49
    /********************************************************************\
50
    | Magic methods
51
    \********************************************************************/
52
53
    /**
54
     * @param string $name
55
     * @param array $arguments
56
     * @return mixed
57
     */
58 2
    public function __call($name, array $arguments)
59
    {
60 2
        if (substr($name, 0, 5) === 'scope') {
61 2
            $scopeName = lcfirst(substr($name, 5));
62 2
            $scope = $this->structure->getScope($this->selection->getName(), $scopeName);
63 2
            if (!$scope) {
64
                trigger_error('Scope ' . $scopeName . ' is not defined for table ' . $this->selection->getName(), E_USER_ERROR);
65
            }
66 2
            return $this->where(call_user_func_array($scope->getCallback(), $arguments));
67
        }
68
69
        trigger_error('Call to undefined method ' . get_class($this) . '::' . $name . '()', E_USER_ERROR);
70
    }
71
72
    /**********************************************************************\
73
     * Wrapper function - fetch
74
    \**********************************************************************/
75
76
    /**
77
     * Returns row specified by primary key
78
     * @param mixed $key    Primary key
79
     * @return mixed
80
     */
81 2
    public function get($key)
82
    {
83 2
        $row = $this->selection->get($key);
84 2
        return $row instanceof NetteDatabaseActiveRow ? $this->prepareRecord($row) : $row;
85
    }
86
87
    /**
88
     * Returns one record
89
     * @return bool|mixed
90
     */
91 32
    public function fetch()
92
    {
93 32
        $row = $this->selection->fetch();
94 32
        return $row ? $this->prepareRecord($row) : $row;
95
    }
96
97
    /**
98
     * Fetches single field
99
     * @param string|null $column
100
     * @return mixed
101
     */
102 2
    public function fetchField($column = null)
103
    {
104 2
        return $this->selection->fetchField($column);
105
    }
106
107
    /**
108
     * Fetch key => value pairs
109
     * @param string|null $key
110
     * @param string|null $value
111
     * @return array
112
     */
113 2
    public function fetchPairs($key = null, $value = null)
114
    {
115 2
        $result = [];
116
117 2
        $pairs = $this->selection->fetchPairs($key, $value);
118 2
        foreach ($pairs as $k => $v) {
119 2
            $result[$k] = $v instanceof NetteDatabaseActiveRow ? $this->prepareRecord($v) : $v;
120
        }
121 2
        return $result;
122
    }
123
124
    /**
125
     * Returns all records
126
     * @return array
127
     */
128 4
    public function fetchAll()
129
    {
130 4
        return $this->prepareRecords($this->selection->fetchAll());
131
    }
132
133
    /**
134
     * Some examples of usage: https://github.com/nette/utils/blob/master/tests%2FUtils%2FArrays.associate().phpt
135
     * @param string $path
136
     * @return array|\stdClass
137
     */
138 2
    public function fetchAssoc($path)
139
    {
140 2
        return $this->selection->fetchAssoc($path);
141
    }
142
143
    /**********************************************************************\
144
     * Wrapper function - sql selections
145
    \**********************************************************************/
146
147
    /**
148
     * Adds select clause, more calls appends to the end
149
     * @param string $columns   for example "column, MD5(column) AS column_md5"
150
     * @param mixed ...$params
151
     * @return Selection
152
     */
153 4
    public function select($columns, ...$params)
154
    {
155 4
        $this->selection->select($columns, ...$params);
156 4
        return $this;
157
    }
158
159
    /**
160
     * Adds condition for primary key
161
     * @param mixed $key
162
     * @return Selection
163
     */
164 8
    public function wherePrimary($key)
165
    {
166 8
        $this->selection->wherePrimary($key);
167 8
        return $this;
168
    }
169
170
    /**
171
     * Adds where condition, more calls appends with AND
172
     * @param string|string[] $condition
173
     * @param mixed ...$params
174
     * @return Selection
175
     */
176 14
    public function where($condition, ...$params)
177
    {
178 14
        $this->selection->where($condition, ...$params);
179 14
        return $this;
180
    }
181
182
    /**
183
     * Adds ON condition when joining specified table, more calls appends with AND
184
     * @param string $tableChain    table chain or table alias for which you need additional left join condition
185
     * @param string $condition     condition possibly containing ?
186
     * @param mixed ...$params
187
     * @return Selection
188
     */
189
    public function joinWhere($tableChain, $condition, ...$params)
190
    {
191
        $this->selection->joinWhere($tableChain, $condition, ...$params);
192
        return $this;
193
    }
194
195
    /**
196
     * Adds where condition using the OR operator between parameters
197
     * More calls appends with AND.
198
     * @param array $parameters     ['column1' => 1, 'column2 > ?' => 2, 'full condition']
199
     * @return Selection
200
     * @throws InvalidArgumentException
201
     */
202 2
    public function whereOr(array $parameters)
203
    {
204 2
        $this->selection->whereOr($parameters);
205 2
        return $this;
206
    }
207
208
    /**
209
     * Adds order clause, more calls appends to the end
210
     * @param string $columns       for example 'column1, column2 DESC'
211
     * @param mixed ...$params
212
     * @return Selection
213
     */
214 4
    public function order($columns, ...$params)
215
    {
216 4
        $this->selection->order($columns, ...$params);
217 4
        return $this;
218
    }
219
220
    /**
221
     * Sets limit clause, more calls rewrite old values
222
     * @param int $limit
223
     * @param int $offset
224
     * @return Selection
225
     */
226 2
    public function limit($limit, $offset = null)
227
    {
228 2
        $this->selection->limit($limit, $offset);
229 2
        return $this;
230
    }
231
232
    /**
233
     * Sets offset using page number, more calls rewrite old values
234
     * @param int $page
235
     * @param int $itemsPerPage
236
     * @param int|null $numOfPages
237
     * @return Selection
238
     */
239 2
    public function page($page, $itemsPerPage, & $numOfPages = null)
240
    {
241 2
        $this->selection->page($page, $itemsPerPage, $numOfPages);
242 2
        return $this;
243
    }
244
245
    /**
246
     * Sets group clause, more calls rewrite old value
247
     * @param string $columns
248
     * @param mixed ...$params
249
     * @return Selection
250
     */
251 2
    public function group($columns, ...$params)
252
    {
253 2
        $this->selection->group($columns, ...$params);
254 2
        return $this;
255
    }
256
257
    /**
258
     * Sets having clause, more calls rewrite old value
259
     * @param string $having
260
     * @param mixed ...$params
261
     * @return Selection
262
     */
263 2
    public function having($having, ...$params)
264
    {
265 2
        $this->selection->having($having, ...$params);
266 2
        return $this;
267
    }
268
269
    /**
270
     * Aliases table. Example ':book:book_tag.tag', 'tg'
271
     * @param string $tableChain
272
     * @param string $alias
273
     * @return Selection
274
     */
275
    public function alias($tableChain, $alias)
276
    {
277
        $this->selection->alias($tableChain, $alias);
278
        return $this;
279
    }
280
281
    /**********************************************************************\
282
     * Wrapper function - aggregations
283
    \**********************************************************************/
284
285
    /**
286
     * Executes aggregation function
287
     * @param string $function      select call in "FUNCTION(column)" format
288
     * @return string
289
     */
290 2
    public function aggregation($function)
291
    {
292 2
        return $this->selection->aggregation($function);
293
    }
294
295
    /**
296
     * Counts number of rows
297
     * Countable interface
298
     * @param string $column    If it is not provided returns count of result rows, otherwise runs new sql counting query
299
     * @return int
300
     */
301 14
    public function count($column = null)
302
    {
303 14
        return $this->selection->count($column);
304
    }
305
306
    /**
307
     * Returns minimum value from a column
308
     * @param string $column
309
     * @return int
310
     */
311 2
    public function min($column)
312
    {
313 2
        return $this->selection->min($column);
314
    }
315
316
    /**
317
     * Returns maximum value from a column
318
     * @param string $column
319
     * @return int
320
     */
321 2
    public function max($column)
322
    {
323 2
        return $this->selection->max($column);
324
    }
325
326
    /**
327
     * Returns sum of values in a column
328
     * @param string $column
329
     * @return int
330
     */
331 2
    public function sum($column)
332
    {
333 2
        return $this->selection->sum($column);
334
    }
335
336
    /**********************************************************************\
337
     * Wrapper function - manipulation
338
    \**********************************************************************/
339
340
    /**
341
     * Inserts row in a table
342
     * @param  array|Traversable|Selection $data
343
     * @return IRow|int|bool
344
     */
345 2
    public function insert($data)
346
    {
347 2
        $insertResult = $this->selection->insert($data);
348 2
        return $insertResult instanceof IRow ? $this->prepareRecord($insertResult) : $insertResult;
349
    }
350
351
    /**
352
     * Updates all rows in result set
353
     * @param  array|Traversable $data      ($column => $value)
354
     * @return int
355
     */
356 2
    public function update($data)
357
    {
358 2
        return $this->selection->update($data);
359
    }
360
361
    /**
362
     * Deletes all rows in result set
363
     * @return int
364
     */
365 2
    public function delete()
366
    {
367 2
        return $this->selection->delete();
368
    }
369
370
    /**********************************************************************\
371
     * Iterator interface
372
    \**********************************************************************/
373
374
    /**
375
     * Rewind selection
376
     */
377 12
    public function rewind()
378
    {
379 12
        $this->selection->rewind();
380 12
    }
381
382
    /**
383
     * Returns current selection data record
384
     * @return bool|mixed
385
     */
386 12
    public function current()
387
    {
388 12
        $row = $this->selection->current();
389 12
        return $row instanceof IRow ? $this->prepareRecord($row) : false;
390
    }
391
392
    /**
393
     * Returns current selection data key
394
     * @return string
395
     */
396 2
    public function key()
397
    {
398 2
        return $this->selection->key();
399
    }
400
401
    /**
402
     * Move iterator
403
     */
404 12
    public function next()
405
    {
406 12
        $this->selection->next();
407 12
    }
408
409
    /**
410
     * It is selection valid
411
     * @return bool
412
     */
413 12
    public function valid()
414
    {
415 12
        return $this->selection->valid();
416
    }
417
418
    /**********************************************************************\
419
     * ArrayAccess interface
420
    \**********************************************************************/
421
422
    /**
423
     * @param string $key   Row ID
424
     * @param IRow $value
425
     */
426 2
    public function offsetSet($key, $value)
427
    {
428 2
        $this->selection->offsetSet($key, $value);
429 2
    }
430
431
    /**
432
     * Returns specified row
433
     * @param string $key   Row ID
434
     * @return IRow|null
435
     */
436 2
    public function offsetGet($key)
437
    {
438 2
        $row = $this->selection->offsetGet($key);
439 2
        return $row instanceof IRow ? $this->prepareRecord($row) : $row;
440
    }
441
442
    /**
443
     * Tests if row exists
444
     * @param string $key   Row ID
445
     * @return bool
446
     */
447 2
    public function offsetExists($key)
448
    {
449 2
        return $this->selection->offsetExists($key);
450
    }
451
452
    /**
453
     * Removes row from result set
454
     * @param string $key   Row ID
455
     */
456 2
    public function offsetUnset($key)
457
    {
458 2
        $this->selection->offsetUnset($key);
459 2
    }
460
461
    /**********************************************************************\
462
     * Build methods
463
    \**********************************************************************/
464
465
    /**
466
     * Prepare one record
467
     * @param IRow $row
468
     * @return mixed
469
     */
470 48
    protected function prepareRecord(IRow $row)
471
    {
472 48
        $recordClass = $this->structure->getActiveRowClass($row->getTable()->getName());
473 48
        return new $recordClass($row, $this->structure);
474
    }
475
476
    /**
477
     * Prepare records array
478
     * @param array $rows
479
     * @return array
480
     */
481 4
    protected function prepareRecords(array $rows)
482
    {
483 4
        $result = [];
484 4
        foreach ($rows as $row) {
485 4
            $result[] = $this->prepareRecord($row);
486
        }
487 4
        return $result;
488
    }
489
}
490