Completed
Push — devops/catch-breaking-changes-... ( 88c9a6 )
by Bas
28s queued 18s
created

Grammar::compileSelect()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 17
ccs 3
cts 3
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace LaravelFreelancerNL\Aranguent\Query;
4
5
use Illuminate\Support\Arr;
6
use Illuminate\Support\Facades\DB;
7
use Illuminate\Support\Traits\Macroable;
8
use LaravelFreelancerNL\Aranguent\Query\Concerns\CompilesAggregates;
9
use LaravelFreelancerNL\Aranguent\Query\Concerns\CompilesColumns;
10
use LaravelFreelancerNL\Aranguent\Query\Concerns\CompilesGroups;
11
use LaravelFreelancerNL\Aranguent\Query\Concerns\CompilesJoins;
12
use LaravelFreelancerNL\Aranguent\Query\Concerns\CompilesWhereClauses;
13
use LaravelFreelancerNL\Aranguent\Query\Concerns\ConvertsIdToKey;
14
use LaravelFreelancerNL\Aranguent\Query\Concerns\HasAliases;
15
use LaravelFreelancerNL\FluentAQL\Exceptions\BindException as BindException;
16
use LaravelFreelancerNL\FluentAQL\Expressions\FunctionExpression;
17
use LaravelFreelancerNL\FluentAQL\Grammar as FluentAqlGrammar;
18
19
class Grammar extends FluentAqlGrammar
20
{
21
    use CompilesAggregates;
22
    use CompilesColumns;
0 ignored issues
show
introduced by
The trait LaravelFreelancerNL\Aran...oncerns\CompilesColumns requires some properties which are not provided by LaravelFreelancerNL\Aranguent\Query\Grammar: $aggregate, $groups, $distinct, $from, $joins, $table
Loading history...
23
    use CompilesJoins;
0 ignored issues
show
introduced by
The trait LaravelFreelancerNL\Aran...\Concerns\CompilesJoins requires some properties which are not provided by LaravelFreelancerNL\Aranguent\Query\Grammar: $type, $table
Loading history...
24
    use CompilesGroups;
25
    use CompilesWhereClauses;
26
    use ConvertsIdToKey;
27
    use HasAliases;
0 ignored issues
show
introduced by
The trait LaravelFreelancerNL\Aran...ery\Concerns\HasAliases requires some properties which are not provided by LaravelFreelancerNL\Aranguent\Query\Grammar: $variables, $groups, $from
Loading history...
28
    use Macroable;
29
30
    public $name;
31
32
    /**
33
     * The grammar table prefix.
34
     *
35
     * @var string
36
     */
37
    protected $tablePrefix = '';
38
39
    /**
40
     * The grammar table prefix.
41
     *
42
     * @var null|int
43
     */
44
    protected $offset = null;
45
46
    /**
47
     * The components that make up a select clause.
48
     *
49
     * @var array
50
     */
51
    protected $selectComponents = [
52
        'from',
53
        'search',
54
        'variables',
55
        'joins',
56
        'wheres',
57
        'groups',
58
        'aggregate',
59
        'havings',
60
        'orders',
61
        'offset',
62
        'limit',
63
        'columns',
64
    ];
65
66
    protected $operatorTranslations = [
67
        '='          => '==',
68
        '<>'         => '!=',
69
        '<=>'        => '==',
70
        'rlike'      => '=~',
71
        'not rlike'  => '!~',
72
        'regexp'     => '=~',
73
        'not regexp' => '!~',
74
    ];
75
76
    protected $whereTypeOperators = [
77
        'In'    => 'IN',
78
        'NotIn' => 'NOT IN',
79
    ];
80
81
    /**
82
     * The grammar specific bitwise operators.
83
     *
84
     * @var array
85
     */
86
    protected $bitwiseOperators = [];
87
88
    /**
89
     * Get the format for database stored dates.
90
     *
91
     * @return string
92
     */
93 26
    public function getDateFormat()
94
    {
95 26
        return 'Y-m-d\TH:i:s.v\Z';
96
    }
97
98
    /**
99
     * Get the grammar specific operators.
100
     *
101
     * @return array
102
     */
103 13
    public function getOperators()
104
    {
105 13
        return $this->comparisonOperators;
106
    }
107
108 156
    protected function prefixTable($table)
109
    {
110 156
        return $this->tablePrefix . $table;
111
    }
112
113
    /**
114
     * Compile an insert statement into AQL.
115
     *
116
     * @param Builder $builder
117
     * @param array   $values
118
     *
119
     * @throws BindException
120
     *
121
     * @return Builder
122
     */
123 9
    public function compileInsert(Builder $builder, array $values)
124
    {
125 9
        if (Arr::isAssoc($values)) {
126 1
            $values = [$values];
127
        }
128 9
        $table = $this->prefixTable($builder->from);
129
130 9
        if (empty($values)) {
131
            $builder->aqb = $builder->aqb->insert('{}', $table);
0 ignored issues
show
Bug introduced by
The method insert() does not exist on LaravelFreelancerNL\Flue...ions\FunctionExpression. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

131
            /** @scrutinizer ignore-call */ 
132
            $builder->aqb = $builder->aqb->insert('{}', $table);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
132
133
            return $builder;
134
        }
135
136
        // Convert id to _key
137 9
        foreach ($values as $key => $value) {
138 9
            $values[$key] = $this->convertIdToKey($value);
139
        }
140
141 9
        $builder->aqb = $builder->aqb->let('values', $values)
0 ignored issues
show
Bug introduced by
The method let() does not exist on LaravelFreelancerNL\Flue...ions\FunctionExpression. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

141
        $builder->aqb = $builder->aqb->/** @scrutinizer ignore-call */ let('values', $values)

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
142
            ->for('value', 'values')
0 ignored issues
show
Bug introduced by
The method for() does not exist on LaravelFreelancerNL\Flue...ions\FunctionExpression. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

142
            ->/** @scrutinizer ignore-call */ for('value', 'values')

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
143
            ->insert('value', $table)
144
            ->return('NEW._key');
0 ignored issues
show
Bug introduced by
The method return() does not exist on LaravelFreelancerNL\Flue...ions\FunctionExpression. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

144
            ->/** @scrutinizer ignore-call */ return('NEW._key');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
145
146 9
        return $builder;
147
    }
148
149
    /**
150
     * Compile an insert and get ID statement into SQL.
151
     *
152
     * @param array<mixed> $values
153
     */
154 13
    public function compileInsertGetId(Builder $builder, $values, $sequence = "_key"): Builder
155
    {
156 13
        if (Arr::isAssoc($values)) {
157 13
            $values = [$values];
158
        }
159 13
        $table = $this->prefixTable($builder->from);
160
161 13
        if (isset($sequence)) {
162 12
            $sequence = $this->convertIdToKey($sequence);
163
        }
164
165 13
        if (empty($values)) {
166
            $builder->aqb = $builder->aqb->insert('{}', $table)
167
                ->return('NEW.' . $sequence);
168
169
            return $builder;
170
        }
171
172
        // Convert id to _key
173 13
        foreach ($values as $key => $value) {
174 13
            $values[$key] = $this->convertIdToKey($value);
175
        }
176
177 13
        $builder->aqb = $builder->aqb->let('values', $values)
178
            ->for('value', 'values')
179
            ->insert('value', $table)
180 13
            ->return('NEW.' . $sequence);
181
182 13
        return $builder;
183
    }
184
185
    /**
186
     * Compile an insert statement into AQL.
187
     *
188
     * @param Builder $builder
189
     * @param array<mixed> $values
190
     * @return Builder
191
     */
192 4
    public function compileInsertOrIgnore(Builder $builder, array $values)
193
    {
194 4
        if (Arr::isAssoc($values)) {
195 4
            $values = [$values];
196
        }
197 4
        $table = $this->prefixTable($builder->from);
198
199 4
        if (empty($values)) {
200
            $builder->aqb = $builder->aqb->insert('{}', $table);
201
202
            return $builder;
203
        }
204
205
        // Convert id to _key
206 4
        foreach ($values as $key => $value) {
207 4
            $values[$key] = $this->convertIdToKey($value);
208
        }
209
210 4
        $builder->aqb = $builder->aqb->let('values', $values)
211
            ->for('value', 'values')
212
            ->insert('value', $table)
213
            ->options(["ignoreErrors" => true])
0 ignored issues
show
Bug introduced by
The method options() does not exist on LaravelFreelancerNL\Flue...ions\FunctionExpression. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

213
            ->/** @scrutinizer ignore-call */ options(["ignoreErrors" => true])

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
214
            ->return('NEW._key');
215
216 4
        return $builder;
217
    }
218
219
220
    /**
221
     * Compile a select query into AQL.
222
     *
223
     * @param Builder $builder
224
     *
225
     * @return Builder
226
     */
227 151
    public function compileSelect(Builder $builder)
228
    {
229
//        if ($builder->unions && $builder->aggregate) {
230
//            return $this->compileUnionAggregate($builder);
231
//        }
232
233
        // To compile the query, we'll spin through each component of the query and
234
        // see if that component exists. If it does we'll just call the compiler
235
        // function for the component which is responsible for making the SQL.
236
237 151
        $builder = $this->compileComponents($builder);
238
239
//        if ($builder->unions) {
240
//            $sql = $this->wrapUnion($sql).' '.$this->compileUnions($builder);
241
//        }
242
243 151
        return $builder;
244
    }
245
246
    /**
247
     * Compile a truncate table statement into SQL.
248
     *
249
     * @param  Builder  $query
250
     * @return array
251
     */
252 1
    public function compileTruncate(Builder $query)
253
    {
254
        /** @phpstan-ignore-next-line */
255 1
        $aqb = DB::aqb();
256 1
        $aqb = $aqb->for('doc', $query->from)->remove('doc', $query->from)->get();
257 1
        return [$aqb->query => []];
258
    }
259
260
    /**
261
     * Compile the components necessary for a select clause.
262
     *
263
     * @param Builder $builder
264
     *
265
     * @return Builder
266
     */
267 151
    protected function compileComponents(Builder $builder)
268
    {
269 151
        foreach ($this->selectComponents as $component) {
270
            // To compile the query, we'll spin through each component of the query and
271
            // see if that component exists. If it does we'll just call the compiler
272
            // function for the component which is responsible for making the SQL.
273
274 151
            if (isset($builder->$component) && !is_null($builder->$component)) {
275 151
                $method = 'compile' . ucfirst($component);
276
277 151
                $builder = $this->$method($builder, $builder->$component);
278
            }
279
        }
280
281 151
        return $builder;
282
    }
283
284
    /**
285
     * Compile the "from" portion of the query -> FOR in AQL.
286
     *
287
     * @param Builder $builder
288
     * @param string  $table
289
     *
290
     * @return Builder
291
     */
292 151
    protected function compileFrom(Builder $builder, $table)
293
    {
294 151
        $table = $this->prefixTable($table);
295 151
        $alias = $this->registerTableAlias($table);
296
297 151
        $builder->aqb = $builder->aqb->for($alias, $table);
298
299 151
        return $builder;
300
    }
301
302
    /**
303
     * @param  Builder  $builder
304
     * @param  array $variables
305
     * @return Builder
306
     */
307 151
    protected function compileVariables(Builder $builder, array $variables)
308
    {
309 151
        if (! empty($variables)) {
310 1
            foreach ($variables as $variable => $data) {
311 1
                $builder->aqb = $builder->aqb->let($variable, $data);
312
            }
313
        }
314
315 151
        return $builder;
316
    }
317
318
    /**
319
     * Compile the "order by" portions of the query.
320
     *
321
     * @param Builder $builder
322
     * @param array   $orders
323
     *
324
     * @return Builder
325
     */
326 3
    protected function compileOrders(Builder $builder, $orders)
327
    {
328 3
        if (!empty($orders)) {
329 3
            $orders = $this->compileOrdersToFlatArray($builder, $orders);
330 3
            $builder->aqb = $builder->aqb->sort(...$orders);
0 ignored issues
show
Bug introduced by
The method sort() does not exist on LaravelFreelancerNL\Flue...ions\FunctionExpression. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

330
            /** @scrutinizer ignore-call */ 
331
            $builder->aqb = $builder->aqb->sort(...$orders);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
331
332 3
            return $builder;
333
        }
334
335
        return $builder;
336
    }
337
338
    /**
339
     * Compile the query orders to an array.
340
     *
341
     * @param Builder $builder
342
     * @param array   $orders
343
     *
344
     * @return array
345
     */
346 3
    protected function compileOrdersToFlatArray(Builder $builder, $orders)
347
    {
348 3
        $flatOrders = [];
349
350 3
        foreach ($orders as $order) {
351 3
            if (!isset($order['type']) || $order['type'] != 'Raw') {
352 2
                $order['column'] = $this->normalizeColumn($builder, $order['column']);
353
            }
354
355 3
            $flatOrders[] = $order['column'];
356
357 3
            if (isset($order['direction'])) {
358 2
                $flatOrders[] = $order['direction'];
359
            }
360
        }
361
362 3
        return $flatOrders;
363
    }
364
365
    /**
366
     * Compile the "offset" portions of the query.
367
     * We are handling this first by saving the offset which will be used by the FluentAQL's limit function.
368
     *
369
     * @param Builder $builder
370
     * @param int     $offset
371
     *
372
     * @return Builder
373
     */
374 6
    protected function compileOffset(Builder $builder, $offset)
375
    {
376 6
        $this->offset = (int) $offset;
377
378 6
        return $builder;
379
    }
380
381
    /**
382
     * Compile the "limit" portions of the query.
383
     *
384
     * @param Builder $builder
385
     * @param int     $limit
386
     *
387
     * @return Builder
388
     */
389 74
    protected function compileLimit(Builder $builder, $limit)
390
    {
391 74
        if ($this->offset !== null) {
392 6
            $builder->aqb = $builder->aqb->limit((int) $this->offset, (int) $limit);
0 ignored issues
show
Bug introduced by
The method limit() does not exist on LaravelFreelancerNL\Flue...ions\FunctionExpression. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

392
            /** @scrutinizer ignore-call */ 
393
            $builder->aqb = $builder->aqb->limit((int) $this->offset, (int) $limit);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
393
394 6
            return $builder;
395
        }
396 68
        $builder->aqb = $builder->aqb->limit((int) $limit);
397
398 68
        return $builder;
399
    }
400
401
402
    /**
403
     * Compile an update statement into SQL.
404
     *
405
     * @param Builder $builder
406
     * @param array   $values
407
     *
408
     * @return Builder
409
     */
410 23
    public function compileUpdate(Builder $builder, array $values)
411
    {
412
413 23
        $table = $this->prefixTable($builder->from);
414 23
        $tableAlias = $this->generateTableAlias($table);
415
416 23
        $builder->aqb = $builder->aqb->for($tableAlias, $table);
417
418
        //Fixme: joins?
419 23
        $builder = $this->compileWheres($builder);
420
421 23
        $builder->aqb = $builder->aqb->update($tableAlias, $values, $table);
0 ignored issues
show
Bug introduced by
The method update() does not exist on LaravelFreelancerNL\Flue...ions\FunctionExpression. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

421
        /** @scrutinizer ignore-call */ 
422
        $builder->aqb = $builder->aqb->update($tableAlias, $values, $table);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
422
423 23
        return $builder;
424
    }
425
426
    /**
427
     * Compile an "upsert" statement into SQL.
428
     *
429
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
430
     *
431
     * @param Builder $query
432
     * @param array $values
433
     * @param array $uniqueBy
434
     * @param array $update
435
     * @return string
436
     */
437 1
    public function compileUpsert(Builder $query, array $values, array $uniqueBy, array $update)
438
    {
439
        // Convert id to _key
440 1
        foreach ($values as $key => $value) {
441 1
            $values[$key] = $this->convertIdToKey($value);
442
        }
443
444 1
        foreach ($uniqueBy as $key => $value) {
445 1
            $uniqueBy[$key] = $this->convertIdToKey($value);
446
        }
447
448 1
        foreach ($update as $key => $value) {
449 1
            $update[$key] = $this->convertIdToKey($value);
450
        }
451
452
        /** @phpstan-ignore-next-line */
453 1
        return DB::aqb()
454
            ->let('docs', $values)
455
            ->for('doc', 'docs')
456 1
            ->insert('doc', $query->from)
457
            ->options([
458
                "overwriteMode" => "update",
459
                "mergeObjects" => false,
460
            ])->get();
461
    }
462
463
    /**
464
     * Compile a delete statement into SQL.
465
     *
466
     * @SuppressWarnings(PHPMD.CamelCaseParameterName)
467
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
468
     *
469
     * @param Builder $builder
470
     * @param null    $id
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $id is correct as it would always require null to be passed?
Loading history...
471
     *
472
     * @return Builder
473
     */
474 10
    public function compileDelete(Builder $builder, $id = null)
475
    {
476 10
        $table = $this->prefixTable($builder->from);
477 10
        $tableAlias = $this->generateTableAlias($table);
478
479
480 10
        if (!is_null($id)) {
0 ignored issues
show
introduced by
The condition is_null($id) is always true.
Loading history...
481 1
            $builder->aqb = $builder->aqb->remove((string) $id, $table);
482
483 1
            return $builder;
484
        }
485
486 10
        $builder->aqb = $builder->aqb->for($tableAlias, $table);
487
488
        //Fixme: joins?
489 10
        $builder = $this->compileWheres($builder);
490
491 10
        $builder->aqb = $builder->aqb->remove($tableAlias, $table);
0 ignored issues
show
Bug introduced by
The method remove() does not exist on LaravelFreelancerNL\Flue...ions\FunctionExpression. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

491
        /** @scrutinizer ignore-call */ 
492
        $builder->aqb = $builder->aqb->remove($tableAlias, $table);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
492
493 10
        return $builder;
494
    }
495
496
    /**
497
     * Compile the random statement into SQL.
498
     *
499
     * @param Builder $builder
500
     *
501
     * @return FunctionExpression;
502
     */
503 1
    public function compileRandom(Builder $builder)
504
    {
505 1
        return $builder->aqb->rand();
0 ignored issues
show
Bug introduced by
The method rand() does not exist on LaravelFreelancerNL\Flue...ions\FunctionExpression. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

505
        return $builder->aqb->/** @scrutinizer ignore-call */ rand();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
506
    }
507
508
    /**
509
     * @param Builder $builder
510
     * @return Builder
511
     */
512 7
    public function compileSearch(Builder $builder): Builder
513
    {
514 7
        $builder->aqb = $builder->aqb->search($builder->search['predicates']);
0 ignored issues
show
Bug introduced by
The method search() does not exist on LaravelFreelancerNL\Flue...ions\FunctionExpression. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

514
        /** @scrutinizer ignore-call */ 
515
        $builder->aqb = $builder->aqb->search($builder->search['predicates']);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
515
516 7
        if (isset($builder->search['options'])) {
517 1
            $builder->aqb = $builder->aqb->options($builder->search['options']);
518
        }
519
520 7
        return $builder;
521
    }
522
523
    /**
524
     * Get the value of a raw expression.
525
     *
526
     * @param  \Illuminate\Database\Query\Expression  $expression
527
     * @return string
528
     */
529 1
    public function getValue($expression)
530
    {
531 1
        return $expression->getValue();
532
    }
533
534
    /**
535
     * Get the grammar specific bit operators.
536
     *
537
     * @return array
538
     */
539 2
    public function getBitwiseOperators()
540
    {
541 2
        return $this->bitwiseOperators;
542
    }
543
}
544