Passed
Push — feature/improved-search-alias-... ( 5f1fd6...88fb3e )
by Bas
03:32 queued 16s
created

Grammar::translateOperator()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 3
c 0
b 0
f 0
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 10
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace LaravelFreelancerNL\Aranguent\Query;
6
7
use Illuminate\Database\Query\Builder as IlluminateQueryBuilder;
8
use Illuminate\Database\Query\Expression;
9
use Illuminate\Database\Query\Grammars\Grammar as IlluminateQueryGrammar;
10
use Illuminate\Support\Arr;
11
use Illuminate\Support\Traits\Macroable;
12
use LaravelFreelancerNL\Aranguent\Query\Concerns\CompilesAggregates;
13
use LaravelFreelancerNL\Aranguent\Query\Concerns\CompilesColumns;
14
use LaravelFreelancerNL\Aranguent\Query\Concerns\CompilesFilters;
15
use LaravelFreelancerNL\Aranguent\Query\Concerns\CompilesGroups;
16
use LaravelFreelancerNL\Aranguent\Query\Concerns\CompilesDataManipulations;
17
use LaravelFreelancerNL\Aranguent\Query\Concerns\CompilesJoins;
18
use LaravelFreelancerNL\Aranguent\Query\Concerns\CompilesUnions;
19
use LaravelFreelancerNL\Aranguent\Query\Concerns\ConvertsIdToKey;
20
use LaravelFreelancerNL\Aranguent\Query\Concerns\HandlesAqlGrammar;
21
22
class Grammar extends IlluminateQueryGrammar
23
{
24
    use CompilesAggregates;
0 ignored issues
show
Bug introduced by
The trait LaravelFreelancerNL\Aran...erns\CompilesAggregates requires the property $from which is not provided by LaravelFreelancerNL\Aranguent\Query\Grammar.
Loading history...
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: $groups, $returnSingleValue, $distinct, $from, $joins, $table, $tableAliases
Loading history...
26
    use CompilesFilters;
0 ignored issues
show
introduced by
The trait LaravelFreelancerNL\Aran...oncerns\CompilesFilters requires some properties which are not provided by LaravelFreelancerNL\Aranguent\Query\Grammar: $havings, $wheres
Loading history...
27
    use CompilesDataManipulations;
0 ignored issues
show
introduced by
The trait LaravelFreelancerNL\Aran...mpilesDataManipulations requires some properties which are not provided by LaravelFreelancerNL\Aranguent\Query\Grammar: $from, $joins
Loading history...
28
    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: $table, $grammar, $type
Loading history...
29
    use CompilesGroups;
30
    use CompilesUnions;
0 ignored issues
show
introduced by
The trait LaravelFreelancerNL\Aran...Concerns\CompilesUnions requires some properties which are not provided by LaravelFreelancerNL\Aranguent\Query\Grammar: $unions, $unionOrders, $unionOffset, $unionLimit
Loading history...
31
    use ConvertsIdToKey;
32
    use HandlesAqlGrammar;
33
    use Macroable;
34
35
    public string $name;
36
37
    /**
38
     * The grammar table prefix.
39
     *
40
     * @var string
41
     */
42
    protected $tablePrefix = '';
43
44
    /**
45
     * The grammar table prefix.
46
     *
47
     * @var null|int
48
     */
49
    protected $offset = null;
50
51
    /**
52
     * The grammar specific operators.
53
     *
54
     * @var array<string>
55
     */
56
    protected $operators = [
57
        '==', '!=', '<', '>', '<=', '>=',
58
        'LIKE', '~', '!~',
59
        'IN', 'NOT IN',
60
        'ALL ==', 'ALL !=', 'ALL <', 'ALL >', 'ALL <=', 'ALL >=', 'ALL IN',
61
        'ANY ==', 'ANY !=', 'ANY <', 'ANY >', 'ANY <=', 'ANY >=', 'ANY IN',
62
        'NONE ==', 'NONE !=', 'NONE <', 'NONE >', 'NONE <=', 'NONE >=', 'NONE IN',
63
    ];
64
65
    /**
66
     * The components that make up a select clause.
67
     *
68
     * @var array<string>
69
     */
70
    protected $selectComponents = [
71
        'preIterationVariables',
72
        'from',
73
        'search',
74
        'joins',
75
        'postIterationVariables',
76
        'wheres',
77
        'groups',
78
        'aggregate',
79
        'havings',
80
        'orders',
81
        'offset',
82
        'limit',
83
        'columns',
84
    ];
85
86
    /**
87
     * @var array<string, string>
88
     */
89
    protected array $operatorTranslations = [
90
        '='          => '==',
91
        '<>'         => '!=',
92
        '<=>'        => '==',
93
        'rlike'      => '=~',
94
        'not rlike'  => '!~',
95
        'regexp'     => '=~',
96
        'not regexp' => '!~',
97
    ];
98
99
    /**
100
     * @var array<string, string>
101
     */
102
    protected array $whereTypeOperators = [
103
        'In'    => 'IN',
104
        'NotIn' => 'NOT IN',
105
    ];
106
107
    /**
108
     * The grammar specific bitwise operators.
109
     *
110
     * @var array<string>
111
     */
112
    public $bitwiseOperators = [
113
        '&', '|', '^', '<<', '>>', '~',
114
    ];
115
116
    /**
117
     * Get the format for database stored dates.
118
     *
119
     * @return string
120
     */
121 26
    public function getDateFormat()
122
    {
123 26
        return 'Y-m-d\TH:i:s.vp';
124
    }
125
126
    /**
127
     * Get the grammar specific operators.
128
     *
129
     * @return array<string>
130
     */
131 92
    public function getOperators()
132
    {
133 92
        return $this->operators;
134
    }
135
136
137 156
    public function translateOperator(string $operator): string
138
    {
139 156
        if (array_key_exists($operator, $this->operatorTranslations)) {
140 94
            return $this->operatorTranslations[$operator];
141
        }
142
143 103
        return $operator;
144
    }
145
146 257
    protected function prefixTable(string $table): string
147
    {
148 257
        return $this->tablePrefix . $table;
149
    }
150
151
152
    /**
153
     * Get the appropriate query parameter place-holder for a value.
154
     *
155
     * @param  mixed  $value
156
     * @return string
157
     */
158 151
    public function parameter($value)
159
    {
160
161 151
        return $this->isExpression($value) ? $this->getValue($value) : $value;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->isExpressi...tValue($value) : $value also could return the type boolean which is incompatible with the documented return type string.
Loading history...
162
    }
163
164
    /**
165
     * Compile the components necessary for a select clause.
166
     *
167
     * @param  IlluminateQueryBuilder  $query
168
     * @return array<string, string>
169
     */
170 256
    protected function compileComponents(IlluminateQueryBuilder $query)
171
    {
172 256
        $aql = [];
173
174 256
        foreach ($this->selectComponents as $component) {
175 256
            if ($component === 'unions') {
176
                continue;
177
            }
178
179 256
            if (isset($query->$component)) {
180 256
                $method = 'compile' . ucfirst($component);
181
182 256
                $aql[$component] = $this->$method($query, $query->$component);
183
            }
184
        }
185
186 256
        return $aql;
187
    }
188
189
190
191
    /**
192
     * Compile a select query into SQL.
193
     *
194
     * @param IlluminateQueryBuilder $query
195
     * @return string
196
     */
197 256
    public function compileSelect(IlluminateQueryBuilder $query)
198
    {
199
        assert($query instanceof Builder);
200
201
        // If the query does not have any columns set, we'll set the columns to the
202
        // * character to just get all of the columns from the database. Then we
203
        // can build the query and concatenate all the pieces together as one.
204 256
        $original = $query->columns;
205
206 256
        if (empty($query->columns)) {
207 26
            $query->columns = ['*'];
208
        }
209
210
        // To compile the query, we'll spin through each component of the query and
211
        // see if that component exists. If it does we'll just call the compiler
212
        // function for the component which is responsible for making the SQL.
213
214 256
        $aql = trim(
215 256
            $this->concatenate(
216 256
                $this->compileComponents($query)
217 256
            )
218 256
        );
219
220
        //        if ($query->unions && $query->aggregate) {
221
        //            return $this->compileUnionAggregate($query);
222
        //        }
223 256
        if ($query->unions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $query->unions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
224 5
            return $this->compileUnions($query, $aql);
225
        }
226
227 256
        $query->columns = $original;
228
229 256
        if ($query->groupVariables !== null) {
230 10
            $query->cleanGroupVariables();
231
        }
232
233 256
        return $aql;
234
    }
235
236
    /**
237
     * Compile the "from" portion of the query -> FOR in AQL.
238
     *
239
     * @param IlluminateQueryBuilder $query
240
     * @param string  $table
241
     *
242
     * @return string
243
     */
244 256
    protected function compileFrom(IlluminateQueryBuilder $query, $table)
245
    {
246
        assert($query instanceof Builder);
247
248
        // FIXME: wrapping/quoting
249 256
        $table = $this->prefixTable($table);
250
251 256
        $alias = $query->registerTableAlias($table);
252
253 256
        $aql = "FOR $alias IN $table";
254
255 256
        if (!empty($query->fromOptions)) {
256 1
            $aql .= $this->compileFromOptions($query->fromOptions);
257
        }
258
259 256
        return $aql;
260
    }
261
262
    /**
263
     * Compile AQL options for the "from" portion of the query.
264
     *
265
     * @param array<mixed> $options
266
     *
267
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
268
     */
269 1
    protected function compileFromOptions($options): string
270
    {
271 1
        return ' OPTIONS ' . $this->generateAqlObject($options);
272
    }
273
274
    /**
275
     * @param IlluminateQueryBuilder $query
276
     * @param array<string, mixed> $variables
277
     * @return string
278
     */
279 256
    protected function compilePreIterationVariables(IlluminateQueryBuilder $query, array $variables): string
280
    {
281 256
        return $this->compileVariables($query, $variables);
282
    }
283
284
    /**
285
     * @param IlluminateQueryBuilder $query
286
     * @param array<string, mixed> $variables
287
     * @return string
288
     */
289 256
    protected function compilePostIterationVariables(IlluminateQueryBuilder $query, array $variables): string
290
    {
291 256
        return $this->compileVariables($query, $variables);
292
    }
293
294
295
    /**
296
     * @param IlluminateQueryBuilder $query
297
     * @param array<string, mixed> $variables
298
     * @return string
299
     *
300
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
301
     */
302 256
    protected function compileVariables(IlluminateQueryBuilder $query, array $variables): string
303
    {
304 256
        $aql = '';
305
306 256
        foreach ($variables as $variable => $value) {
307 7
            if ($value instanceof Expression) {
308
                $value = $value->getValue($this);
309
            }
310
311 7
            $aql .= ' LET ' . $variable . ' = ' . $value;
312
        }
313
314 256
        return trim($aql);
315
    }
316
317
    /**
318
     * Compile the "order by" portions of the query.
319
     *
320
     * @param Builder $query
321
     * @param array<mixed> $orders
322
     * @param null|string $table
323
     * @return string
324
     */
325 35
    protected function compileOrders(IlluminateQueryBuilder $query, $orders, $table = null)
326
    {
327 35
        if (!empty($orders)) {
328 35
            return 'SORT ' . implode(', ', $this->compileOrdersToArray($query, $orders, $table));
329
        }
330
331
        return '';
332
    }
333
334
    /**
335
     * Compile the query orders to an array.
336
     *
337
     * @param Builder $query
338
     * @param array<mixed> $orders
339
     * @param null|string $table
340
     * @return array<string>
341
     * @throws \Exception
342
     */
343 35
    protected function compileOrdersToArray(IlluminateQueryBuilder $query, $orders, $table = null)
344
    {
345 35
        return array_map(function ($order) use ($query, $table) {
346 35
            $key = 'column';
347 35
            if (array_key_exists('sql', $order)) {
348 2
                $key = 'sql';
349
            }
350
351 35
            if (!$order[$key] instanceof Expression) {
352 29
                $order[$key] = $this->normalizeColumn($query, $order[$key], $table);
353
            }
354
355 35
            if ($order[$key] instanceof Expression) {
356 6
                $order[$key] = $order[$key]->getValue($this);
357
            }
358
359 35
            return array_key_exists('direction', $order) ? $order[$key] . ' ' . $order['direction'] : $order[$key];
360 35
        }, $orders);
361
    }
362
363
    /**
364
     * Compile the "offset" portions of the query.
365
     *
366
     * @param  \Illuminate\Database\Query\Builder  $query
367
     * @param  int  $offset
368
     * @return string
369
     *
370
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
371
     */
372 10
    protected function compileOffset(IlluminateQueryBuilder $query, $offset)
373
    {
374 10
        $this->offset = (int) $offset;
375
376 10
        return "";
377
    }
378
379
    /**
380
     * Compile the "limit" portions of the query.
381
     *
382
     * @param  \Illuminate\Database\Query\Builder  $query
383
     * @param  int  $limit
384
     * @return string
385
     *
386
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
387
     */
388 135
    protected function compileLimit(IlluminateQueryBuilder $query, $limit)
389
    {
390 135
        if ($this->offset !== null) {
391 10
            return "LIMIT " . (int) $this->offset . ", " . (int) $limit;
392
        }
393
394 125
        return "LIMIT " . (int) $limit;
395
    }
396
397
    /**
398
     * Compile the random statement into SQL.
399
     *
400
     * @param  string|int|null  $seed
401
     * @return string
402
     */
403 1
    public function compileRandom($seed = null)
404
    {
405 1
        unset($seed);
406
407 1
        return "RAND()";
408
    }
409
410
    /**
411
     * @param IlluminateQueryBuilder $query
412
     * @param array<mixed> $search
413
     * @return string
414
     * @throws \Exception
415
     */
416 6
    public function compileSearch(IlluminateQueryBuilder $query, array $search)
417
    {
418 6
        if (isset($search['expression'])) {
419 1
            return (string) $this->getValue($search['expression']);
420
        }
421
422 5
        $predicates = [];
423 5
        foreach($search['fields'] as $field) {
424 5
            $predicates[] = $this->normalizeColumn($query, $field)
425 5
                . ' IN TOKENS(' . $search['searchText'] . ', "text_en")';
426
        }
427
428 5
        return 'SEARCH ANALYZER('
429 5
            . implode(' OR ', $predicates)
430 5
            . ', "text_en")';
431
    }
432
433
    /**
434
     * Get the value of a raw expression.
435
     *
436
     * @param bool|float|Expression|int|string|null $expression
437
     * @return bool|float|int|string|null
438
     */
439 213
    public function getValue($expression)
440
    {
441 213
        if ($expression instanceof Expression) {
442 2
            return $expression->getValue($this);
443
        }
444
445 213
        return $expression;
446
    }
447
448
    /**
449
     * Get the grammar specific bit operators.
450
     *
451
     * @return array<string>
452
     */
453 139
    public function getBitwiseOperators()
454
    {
455 139
        return $this->bitwiseOperators;
456
    }
457
458
    /**
459
     * Prepare the bindings for a delete statement.
460
     *
461
     * @param  array<mixed>  $bindings
462
     * @return array<mixed>
463
     */
464 21
    public function prepareBindingsForDelete(array $bindings)
465
    {
466 21
        return Arr::collapse(
467 21
            Arr::except($bindings, 'select')
468 21
        );
469
    }
470
471
    /**
472
     * Determine if the given value is a raw expression.
473
     *
474
     * @param  mixed  $value
475
     * @return bool
476
     */
477 151
    public function isExpression($value)
478
    {
479 151
        return $value instanceof Expression;
480
    }
481
}
482