Passed
Push — next ( 480502...54a2e9 )
by Bas
12:53
created

Grammar::compileSearch()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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

126
            /** @scrutinizer ignore-call */ 
127
            $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...
127
128
            return $builder;
129
        }
130
131 44
        $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

131
        $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...
132 44
            ->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

132
            ->/** @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...
133 44
            ->insert('value', $table)
134 44
            ->return('NEW._id');
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

134
            ->/** @scrutinizer ignore-call */ return('NEW._id');

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...
135
136 44
        return $builder;
137
    }
138
139
    /**
140
     * Compile an insert and get ID statement into SQL.
141
     *
142
     * @param array<mixed> $values
143
     */
144 10
    public function compileInsertGetId(Builder $builder, $values, $sequence = "_id"): Builder
145
    {
146 10
        if (Arr::isAssoc($values)) {
147 10
            $values = [$values];
148
        }
149 10
        $table = $this->prefixTable($builder->from);
150
151 10
        if (empty($values)) {
152
            $builder->aqb = $builder->aqb->insert('{}', $table)
153
                ->return('NEW.' . $sequence);
154
155
            return $builder;
156
        }
157
158 10
        $builder->aqb = $builder->aqb->let('values', $values)
159 10
            ->for('value', 'values')
160 10
            ->insert('value', $table)
161 10
            ->return('NEW.' . $sequence);
162
163 10
        return $builder;
164
    }
165
166
    /**
167
     * Compile an insert statement into AQL.
168
     *
169
     * @param Builder $builder
170
     * @param array<mixed> $values
171
     * @return Builder
172
     */
173 88
    public function compileInsertOrIgnore(Builder $builder, array $values)
174
    {
175 88
        if (Arr::isAssoc($values)) {
176 88
            $values = [$values];
177
        }
178 88
        $table = $this->prefixTable($builder->from);
179
180 88
        if (empty($values)) {
181
            $builder->aqb = $builder->aqb->insert('{}', $table);
182
183
            return $builder;
184
        }
185
186 88
        $builder->aqb = $builder->aqb->let('values', $values)
187 88
            ->for('value', 'values')
188 88
            ->insert('value', $table)
189 88
            ->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

189
            ->/** @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...
190 88
            ->return('NEW._id');
191
192 88
        return $builder;
193
    }
194
195
196
    /**
197
     * Compile a select query into AQL.
198
     *
199
     * @param Builder $builder
200
     *
201
     * @return Builder
202
     */
203 136
    public function compileSelect(Builder $builder)
204
    {
205
//        if ($builder->unions && $builder->aggregate) {
206
//            return $this->compileUnionAggregate($builder);
207
//        }
208
209
        // To compile the query, we'll spin through each component of the query and
210
        // see if that component exists. If it does we'll just call the compiler
211
        // function for the component which is responsible for making the SQL.
212
213 136
        $builder = $this->compileComponents($builder);
214
215
//        if ($builder->unions) {
216
//            $sql = $this->wrapUnion($sql).' '.$this->compileUnions($builder);
217
//        }
218
219 136
        return $builder;
220
    }
221
222
    /**
223
     * Compile a truncate table statement into SQL.
224
     *
225
     * @param  Builder  $query
226
     * @return array
227
     */
228 1
    public function compileTruncate(Builder $query)
229
    {
230
        /** @phpstan-ignore-next-line */
231 1
        $aqb = DB::aqb();
232 1
        $aqb = $aqb->for('doc', $query->from)->remove('doc', $query->from)->get();
233 1
        return [$aqb->query => []];
234
    }
235
236
    /**
237
     * Compile the components necessary for a select clause.
238
     *
239
     * @param Builder $builder
240
     *
241
     * @return Builder
242
     */
243 136
    protected function compileComponents(Builder $builder)
244
    {
245 136
        foreach ($this->selectComponents as $component) {
246
            // To compile the query, we'll spin through each component of the query and
247
            // see if that component exists. If it does we'll just call the compiler
248
            // function for the component which is responsible for making the SQL.
249
250 136
            if (isset($builder->$component) && !is_null($builder->$component)) {
251 136
                $method = 'compile' . ucfirst($component);
252
253 136
                $builder = $this->$method($builder, $builder->$component);
254
            }
255
        }
256
257 136
        return $builder;
258
    }
259
260
    /**
261
     * Compile the "from" portion of the query -> FOR in AQL.
262
     *
263
     * @param Builder $builder
264
     * @param string  $table
265
     *
266
     * @return Builder
267
     */
268 136
    protected function compileFrom(Builder $builder, $table)
269
    {
270 136
        $table = $this->prefixTable($table);
271 136
        $alias = $this->registerTableAlias($table);
272
273 136
        $builder->aqb = $builder->aqb->for($alias, $table);
274
275 136
        return $builder;
276
    }
277
278
    /**
279
     * @param  Builder  $builder
280
     * @param  array $variables
281
     * @return Builder
282
     */
283 136
    protected function compileVariables(Builder $builder, array $variables)
284
    {
285 136
        if (! empty($variables)) {
286 1
            foreach ($variables as $variable => $data) {
287 1
                $builder->aqb = $builder->aqb->let($variable, $data);
288
            }
289
        }
290
291 136
        return $builder;
292
    }
293
294
    /**
295
     * Compile the "order by" portions of the query.
296
     *
297
     * @param Builder $builder
298
     * @param array   $orders
299
     *
300
     * @return Builder
301
     */
302 3
    protected function compileOrders(Builder $builder, $orders)
303
    {
304 3
        if (!empty($orders)) {
305 3
            $orders = $this->compileOrdersToFlatArray($builder, $orders);
306 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

306
            /** @scrutinizer ignore-call */ 
307
            $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...
307
308 3
            return $builder;
309
        }
310
311
        return $builder;
312
    }
313
314
    /**
315
     * Compile the query orders to an array.
316
     *
317
     * @param Builder $builder
318
     * @param array   $orders
319
     *
320
     * @return array
321
     */
322 3
    protected function compileOrdersToFlatArray(Builder $builder, $orders)
323
    {
324 3
        $flatOrders = [];
325
326 3
        foreach ($orders as $order) {
327 3
            if (!isset($order['type']) || $order['type'] != 'Raw') {
328 2
                $order['column'] = $this->normalizeColumn($builder, $order['column']);
329
            }
330
331 3
            $flatOrders[] = $order['column'];
332
333 3
            if (isset($order['direction'])) {
334 2
                $flatOrders[] = $order['direction'];
335
            }
336
        }
337
338 3
        return $flatOrders;
339
    }
340
341
    /**
342
     * Compile the "offset" portions of the query.
343
     * We are handling this first by saving the offset which will be used by the FluentAQL's limit function.
344
     *
345
     * @param Builder $builder
346
     * @param int     $offset
347
     *
348
     * @return Builder
349
     */
350 4
    protected function compileOffset(Builder $builder, $offset)
351
    {
352 4
        $this->offset = (int) $offset;
353
354 4
        return $builder;
355
    }
356
357
    /**
358
     * Compile the "limit" portions of the query.
359
     *
360
     * @param Builder $builder
361
     * @param int     $limit
362
     *
363
     * @return Builder
364
     */
365 72
    protected function compileLimit(Builder $builder, $limit)
366
    {
367 72
        if ($this->offset !== null) {
368 4
            $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

368
            /** @scrutinizer ignore-call */ 
369
            $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...
369
370 4
            return $builder;
371
        }
372 68
        $builder->aqb = $builder->aqb->limit((int) $limit);
373
374 68
        return $builder;
375
    }
376
377
378
    /**
379
     * Compile an update statement into SQL.
380
     *
381
     * @param Builder $builder
382
     * @param array   $values
383
     *
384
     * @return Builder
385
     */
386 23
    public function compileUpdate(Builder $builder, array $values)
387
    {
388
389 23
        $table = $this->prefixTable($builder->from);
390 23
        $tableAlias = $this->generateTableAlias($table);
391
392 23
        $builder->aqb = $builder->aqb->for($tableAlias, $table);
393
394
        //Fixme: joins?
395 23
        $builder = $this->compileWheres($builder);
396
397 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

397
        /** @scrutinizer ignore-call */ 
398
        $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...
398
399 23
        return $builder;
400
    }
401
402
    /**
403
     * Compile an "upsert" statement into SQL.
404
     *
405
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
406
     *
407
     * @param Builder $query
408
     * @param array $values
409
     * @param array $uniqueBy
410
     * @param array $update
411
     * @return string
412
     */
413 1
    public function compileUpsert(Builder $query, array $values, array $uniqueBy, array $update)
414
    {
415
        /** @phpstan-ignore-next-line */
416 1
        return DB::aqb()
417 1
            ->let('docs', $values)
418 1
            ->for('doc', 'docs')
419 1
            ->insert('doc', $query->from)
420 1
            ->options([
421 1
                "overwriteMode" => "update",
422
                "mergeObjects" => false,
423 1
            ])->get();
424
    }
425
426
    /**
427
     * Compile a delete statement into SQL.
428
     *
429
     * @SuppressWarnings(PHPMD.CamelCaseParameterName)
430
     * @SuppressWarnings(PHPMD.CamelCaseVariableName)
431
     *
432
     * @param Builder $builder
433
     * @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...
434
     *
435
     * @return Builder
436
     */
437 12
    public function compileDelete(Builder $builder, $id = null)
438
    {
439 12
        $table = $this->prefixTable($builder->from);
440 12
        $tableAlias = $this->generateTableAlias($table);
441
442
443 12
        if (!is_null($id)) {
0 ignored issues
show
introduced by
The condition is_null($id) is always true.
Loading history...
444 1
            $builder->aqb = $builder->aqb->remove((string) $id, $table);
445
446 1
            return $builder;
447
        }
448
449 12
        $builder->aqb = $builder->aqb->for($tableAlias, $table);
450
451
        //Fixme: joins?
452 12
        $builder = $this->compileWheres($builder);
453
454 12
        $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

454
        /** @scrutinizer ignore-call */ 
455
        $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...
455
456 12
        return $builder;
457
    }
458
459
    /**
460
     * Compile the random statement into SQL.
461
     *
462
     * @param Builder $builder
463
     *
464
     * @return FunctionExpression;
465
     */
466 1
    public function compileRandom(Builder $builder)
467
    {
468 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

468
        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...
469
    }
470
471
    /**
472
     * @param Builder $builder
473
     * @return Builder|\LaravelFreelancerNL\FluentAQL\QueryBuilder|null
474
     */
475 7
    public function compileSearch(Builder $builder): Builder
476
    {
477 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

477
        /** @scrutinizer ignore-call */ 
478
        $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...
478
479 7
        if (isset($builder->search['options'])) {
480 3
            $builder->aqb = $builder->aqb->options($builder->search['options']);
481
        }
482
483 7
        return $builder;
484
    }
485
486
    /**
487
     * Get the value of a raw expression.
488
     *
489
     * @param  \Illuminate\Database\Query\Expression  $expression
490
     * @return string
491
     */
492 1
    public function getValue($expression)
493
    {
494 1
        return $expression->getValue();
495
    }
496
}
497