Completed
Pull Request — master (#9)
by Samuel
11:57
created

Selection::fetchField()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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