Passed
Push — main ( 440f49...00eeb9 )
by Peter
02:41
created

Select::offset()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 5
rs 10
ccs 3
cts 3
cp 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace QB\Generic\Statement;
6
7
use QB\Generic\Clause\Column;
8
use QB\Generic\Clause\IColumn;
9
use QB\Generic\Clause\IJoin;
10
use QB\Generic\Clause\Join;
11
use QB\Generic\Clause\Table;
12
use QB\Generic\Expr\Expr;
13
use QB\Generic\IQueryPart;
14
15
/**
16
 * @SuppressWarnings(PHPMD.TooManyPublicMethods)
17
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
18
 * SuppressWarnings("complexity")
19
 */
20
class Select implements ISelect
21
{
22
    public const ALL      = 'ALL';
23
    public const DISTINCT = 'DISTINCT';
24
25
    /** @var array<int,string|Table> */
26
    protected array $tables = [];
27
28
    /** @var string[] */
29
    protected array $modifiers = [];
30
31
    /** @var IColumn[] */
32
    protected array $columns = [];
33
34
    /** @var IJoin[] */
35
    protected array $joins = [];
36
37
    /** @var IQueryPart[] */
38
    protected array $whereParts = [];
39
40
    /** @var IQueryPart[] */
41
    protected array $groupByParts = [];
42
43
    /** @var IQueryPart[] */
44
    protected array $havingParts = [];
45
46
    /** @var array<string,string> */
47
    protected array $orderBy = [];
48
49
    protected ?int $offset = null;
50
51
    protected ?int $limit = null;
52
53
    /**
54
     * Select constructor.
55
     *
56
     * @param string|IColumn ...$columns
57
     */
58 67
    public function __construct(string|IColumn ...$columns)
59
    {
60 67
        $this->columns(...$columns);
61 67
    }
62
63
    /**
64
     * @param string|Table ...$tables
65
     *
66
     * @return $this
67
     */
68 51
    public function from(string|Table ...$tables): static
69
    {
70 51
        $this->tables = array_merge($this->tables, $tables);
71
72 51
        return $this;
73
    }
74
75
    /**
76
     * @param string ...$modifiers
77
     *
78
     * @return $this
79
     */
80 5
    public function modifier(string ...$modifiers): static
81
    {
82 5
        $this->modifiers = array_merge($this->modifiers, $modifiers);
83
84 5
        return $this;
85
    }
86
87
    /**
88
     * @param string|IQueryPart $column
89
     * @param string|null       $alias
90
     *
91
     * @return $this
92
     */
93 28
    public function addColumn(string|IQueryPart $column, ?string $alias = null): static
94
    {
95 28
        $this->columns[] = new Column($column, $alias);
96
97 28
        return $this;
98
    }
99
100
    /**
101
     * @param string|IColumn ...$columns
102
     *
103
     * @return $this
104
     */
105 67
    public function columns(string|IColumn ...$columns): static
106
    {
107 67
        foreach ($columns as $column) {
108 16
            if ($column instanceof IColumn) {
109 8
                $this->columns[] = $column;
110 8
                continue;
111
            }
112
113 12
            if (strpos($column, ' AS ')) {
114 5
                $parts = explode(' AS ', $column);
115
116 5
                $this->columns[] = new Column($parts[0], $parts[1]);
117
            } else {
118 12
                $this->columns[] = new Column($column, null);
119
            }
120
        }
121
122 67
        return $this;
123
    }
124
125
    /**
126
     * @param string            $table
127
     * @param string|IQueryPart $on
128
     * @param string|null       $alias
129
     *
130
     * @return $this
131
     */
132 8
    public function innerJoin(string $table, string|IQueryPart $on, ?string $alias = null): static
133
    {
134 8
        $this->joins[] = new Join(IJoin::TYPE_INNER_JOIN, $table, $on, $alias);
135
136 8
        return $this;
137
    }
138
139
    /**
140
     * @param string            $table
141
     * @param string|IQueryPart $on
142
     * @param string|null       $alias
143
     *
144
     * @return $this
145
     */
146 6
    public function leftJoin(string $table, string|IQueryPart $on, ?string $alias = null): static
147
    {
148 6
        $this->joins[] = new Join(IJoin::TYPE_LEFT_JOIN, $table, $on, $alias);
149
150 6
        return $this;
151
    }
152
153
    /**
154
     * @param string            $table
155
     * @param string|IQueryPart $on
156
     * @param string|null       $alias
157
     *
158
     * @return $this
159
     */
160 3
    public function rightJoin(string $table, string|IQueryPart $on, ?string $alias = null): static
161
    {
162 3
        $this->joins[] = new Join(IJoin::TYPE_RIGHT_JOIN, $table, $on, $alias);
163
164 3
        return $this;
165
    }
166
167
    /**
168
     * @param string            $table
169
     * @param string|IQueryPart $on
170
     * @param string|null       $alias
171
     *
172
     * @return $this
173
     */
174 3
    public function fullJoin(string $table, string|IQueryPart $on, ?string $alias = null): static
175
    {
176 3
        $this->joins[] = new Join(IJoin::TYPE_FULL_JOIN, $table, $on, $alias);
177
178 3
        return $this;
179
    }
180
181
    /**
182
     * @param IJoin ...$joins
183
     *
184
     * @return $this
185
     */
186 3
    public function join(IJoin ...$joins): static
187
    {
188 3
        $this->joins = array_merge($this->joins, $joins);
189
190 3
        return $this;
191
    }
192
193
    /**
194
     * @param string|IQueryPart ...$whereParts
195
     *
196
     * @return $this
197
     */
198 8
    public function where(string|IQueryPart ...$whereParts): static
199
    {
200 8
        foreach ($whereParts as $wherePart) {
201 8
            $wherePart = is_string($wherePart) ? new Expr($wherePart) : $wherePart;
202
203 8
            $this->whereParts[] = $wherePart;
204
        }
205
206 8
        return $this;
207
    }
208
209
    /**
210
     * @param string|IQueryPart ...$groupByParts
211
     *
212
     * @return $this
213
     */
214 8
    public function groupBy(string|IQueryPart ...$groupByParts): static
215
    {
216 8
        foreach ($groupByParts as $groupByPart) {
217 8
            $groupByPart = is_string($groupByPart) ? new Expr($groupByPart) : $groupByPart;
218
219 8
            $this->groupByParts[] = $groupByPart;
220
        }
221
222 8
        return $this;
223
    }
224
225
    /**
226
     * @param string|IQueryPart ...$havingParts
227
     *
228
     * @return $this
229
     */
230 8
    public function having(string|IQueryPart ...$havingParts): static
231
    {
232 8
        foreach ($havingParts as $havingPart) {
233 8
            $havingPart = is_string($havingPart) ? new Expr($havingPart) : $havingPart;
234
235 8
            $this->havingParts[] = $havingPart;
236
        }
237
238 8
        return $this;
239
    }
240
241
    /**
242
     * @param string $column
243
     * @param string $direction
244
     *
245
     * @return $this
246
     */
247 5
    public function orderBy(string $column, string $direction = 'ASC'): static
248
    {
249 5
        $this->orderBy[$column] = $direction;
250
251 5
        return $this;
252
    }
253
254
    /**
255
     * @param int|null $offset
256
     *
257
     * @return $this
258
     */
259 5
    public function offset(?int $offset): static
260
    {
261 5
        $this->offset = $offset;
262
263 5
        return $this;
264
    }
265
266
    /**
267
     * @param int|null $limit
268
     *
269
     * @return $this
270
     */
271 6
    public function limit(?int $limit): static
272
    {
273 6
        $this->limit = $limit;
274
275 6
        return $this;
276
    }
277
278
    /**
279
     * @return string
280
     */
281 61
    public function __toString(): string
282
    {
283 61
        if (!$this->isValid()) {
284 3
            throw new \RuntimeException('under-initialized SELECT query');
285
        }
286
287 58
        $select = $this->getSelect();
288
289 58
        if (count($this->tables) === 0) {
290 17
            return $select;
291
        }
292
293 41
        $parts = array_merge(
294 41
            [$select],
295 41
            $this->getFrom(),
296 41
            $this->getJoin(),
297 41
            $this->getWhere(),
298 41
            $this->getGroupBy(),
299 41
            $this->getHaving(),
300 41
            $this->getOrderBy(),
301 41
            $this->getLimit(),
302
        );
303
304 41
        $parts = array_filter($parts);
305
306 41
        return implode(PHP_EOL, $parts);
307
    }
308
309 61
    public function isValid(): bool
310
    {
311 61
        return count($this->columns) > 0 || count($this->tables) > 0;
312
    }
313
314 58
    protected function getSelect(): string
315
    {
316 58
        $sql   = [];
317 58
        $sql[] = 'SELECT';
318 58
        $sql[] = $this->getModifiers();
319
320 58
        $sql = array_filter($sql);
321
322 58
        $sql = implode(' ', $sql);
323
324 58
        return $sql . ' ' . $this->getColumns();
325
    }
326
327 58
    protected function getColumns(): string
328
    {
329 58
        if (empty($this->columns)) {
330 26
            return '*';
331
        }
332
333 32
        $parts = [];
334 32
        foreach ($this->columns as $column) {
335 32
            $parts[] = (string)$column;
336
        }
337
338 32
        return implode(', ', $parts);
339
    }
340
341 58
    protected function getModifiers(): string
342
    {
343 58
        if (empty($this->modifiers)) {
344 55
            return '';
345
        }
346
347 7
        return implode(' ', $this->modifiers);
348
    }
349
350 41
    protected function getFrom(): array
351
    {
352 41
        return ['FROM ' . implode(', ', $this->tables)];
353
    }
354
355
    /**
356
     * @return string[]
357
     */
358 41
    protected function getJoin(): array
359
    {
360 41
        if (count($this->joins) === 0) {
361 25
            return [];
362
        }
363
364 20
        $parts = [];
365 20
        foreach ($this->joins as $join) {
366 20
            $parts[] = (string)$join;
367
        }
368
369 20
        return $parts;
370
    }
371
372 41
    protected function getWhere(): array
373
    {
374 41
        if (count($this->whereParts) === 0) {
375 39
            return [];
376
        }
377
378 6
        $parts = [];
379 6
        foreach ($this->whereParts as $wherePart) {
380 6
            $parts[] = (string)$wherePart;
381
        }
382
383 6
        return ['WHERE ' . implode(' AND ', $parts)];
384
    }
385
386 41
    protected function getGroupBy(): array
387
    {
388 41
        if (count($this->groupByParts) === 0) {
389 40
            return [];
390
        }
391
392 5
        $parts = [];
393 5
        foreach ($this->groupByParts as $groupByPart) {
394 5
            $parts[] = (string)$groupByPart;
395
        }
396
397 5
        return ['GROUP BY ' . implode(', ', $parts)];
398
    }
399
400 41
    protected function getHaving(): array
401
    {
402 41
        if (count($this->havingParts) === 0) {
403 40
            return [];
404
        }
405
406 5
        $parts = [];
407 5
        foreach ($this->havingParts as $havingPart) {
408 5
            $parts[] = (string)$havingPart;
409
        }
410
411 5
        return ['HAVING ' . implode(' AND ', $parts)];
412
    }
413
414 41
    protected function getOrderBy(): array
415
    {
416 41
        if (count($this->orderBy) === 0) {
417 40
            return [];
418
        }
419
420 5
        $parts = [];
421 5
        foreach ($this->orderBy as $column => $direction) {
422 5
            $parts[] = "$column $direction";
423
        }
424
425 5
        return ['ORDER BY ' . implode(', ', $parts)];
426
    }
427
428 26
    protected function getLimit(): array
429
    {
430 26
        $parts = [];
431 26
        if ($this->offset !== null) {
432 4
            $parts[] = sprintf('OFFSET %d ROWS', $this->offset);
433
        }
434 26
        if ($this->limit !== null) {
435 4
            $parts[] = sprintf('FETCH FIRST %d ROWS ONLY', $this->limit);
436
        }
437
438 26
        return $parts;
439
    }
440
441
    /**
442
     * @return array
443
     */
444 6
    public function getParams(): array
445
    {
446 6
        $params = [];
447
448 6
        foreach ($this->columns as $column) {
449 4
            $params = array_merge($params, $column->getParams());
450
        }
451
452 6
        foreach ($this->joins as $join) {
453 3
            $params = array_merge($params, $join->getParams());
454
        }
455
456 6
        foreach ($this->whereParts as $wherePart) {
457 4
            $params = array_merge($params, $wherePart->getParams());
458
        }
459
460 6
        foreach ($this->groupByParts as $groupByPart) {
461 3
            $params = array_merge($params, $groupByPart->getParams());
462
        }
463
464 6
        foreach ($this->havingParts as $havingPart) {
465 3
            $params = array_merge($params, $havingPart->getParams());
466
        }
467
468 6
        return $params;
469
    }
470
}
471