Failed Conditions
Push — next ( a0f087...d84956 )
by Bas
03:42 queued 15s
created

Grammar::getBitwiseOperators()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
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;
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, $joins, $unions, $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
Bug introduced by
The trait LaravelFreelancerNL\Aran...mpilesDataManipulations requires the property $joins which is not provided by LaravelFreelancerNL\Aranguent\Query\Grammar.
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: $unionOrders, $unionOffset, $unionLimit, $unions
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
        'IN', 'NOT IN', 'LIKE', 'NOT LIKE', '=~', '!~',
59
        'ALL ==', 'ALL !=', 'ALL <', 'ALL >', 'ALL <=', 'ALL >=', 'ALL IN', 'ALL NOT IN',
60
        'ANY ==', 'ANY !=', 'ANY <', 'ANY >', 'ANY <=', 'ANY >=', 'ANY IN', 'ANY NOT IN',
61
        'NONE ==', 'NONE !=', 'NONE <', 'NONE >', 'NONE <=', 'NONE >=', 'NONE IN', 'NONE NOT IN',
62
    ];
63
64
    /**
65
     * The components that make up a select clause.
66
     *
67
     * @var array<string>
68
     */
69
    protected $selectComponents = [
70
        'preIterationVariables',
71
        'from',
72
        'search',
73
        'joins',
74
        'postIterationVariables',
75
        'wheres',
76
        'groups',
77
        'aggregate',
78
        'havings',
79
        'orders',
80
        'offset',
81
        'limit',
82
        'columns',
83
    ];
84
85
    /**
86
     * @var array<string, string>
87
     */
88
    protected array $operatorTranslations = [
89
        '='          => '==',
90
        '<>'         => '!=',
91
        '<=>'        => '==',
92
        'rlike'      => '=~',
93
        'not rlike'  => '!~',
94
        'regexp'     => '=~',
95
        'not regexp' => '!~',
96
    ];
97
98
    /**
99
     * @var array<string, string>
100
     */
101
    protected array $whereTypeOperators = [
102
        'In'    => 'IN',
103
        'NotIn' => 'NOT IN',
104
    ];
105
106
    /**
107
     * The grammar specific bitwise operators.
108
     *
109
     * @var array<string>
110
     */
111
    public $bitwiseOperators = [
112
        '&', '|', '^', '<<', '>>', '~',
113
    ];
114
115
    /**
116
     * Get the format for database stored dates.
117
     *
118
     * @return string
119
     */
120 45
    public function getDateFormat()
121
    {
122 45
        return  config('arangodb.datetime_format');
123
    }
124
125
    /**
126
     * Get the grammar specific operators.
127
     *
128
     * @return array<string>
129
     */
130 180
    public function getOperators()
131
    {
132 180
        return $this->operators;
133
    }
134
135
136 242
    public function translateOperator(string $operator): string
137
    {
138 242
        if (array_key_exists($operator, $this->operatorTranslations)) {
139 111
            return $this->operatorTranslations[$operator];
140
        }
141
142 185
        return $operator;
143
    }
144
145 372
    protected function prefixTable(Expression|bool|float|int|string|null $table): string
146
    {
147 372
        return $this->tablePrefix . $this->getValue($table);
148
    }
149
150
151
    /**
152
     * Get the appropriate query parameter place-holder for a value.
153
     *
154
     * @param  mixed  $value
155
     * @return string
156
     */
157 239
    public function parameter($value)
158
    {
159
160 239
        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...
161
    }
162
163
    /**
164
     * Compile the components necessary for a select clause.
165
     *
166
     * @param  IlluminateQueryBuilder  $query
167
     * @return array<string, string>
168
     */
169 371
    protected function compileComponents(IlluminateQueryBuilder $query)
170
    {
171 371
        $aql = [];
172
173 371
        foreach ($this->selectComponents as $component) {
174 371
            if ($component === 'unions') {
175
                continue;
176
            }
177
178 371
            if ($component === 'aggregate' && $query->unions !== null) {
179 10
                continue;
180
            }
181
182 371
            if (isset($query->$component)) {
183 371
                $method = 'compile' . ucfirst($component);
184
185 371
                $aql[$component] = $this->$method($query, $query->$component);
186
            }
187
        }
188
189 371
        return $aql;
190
    }
191
192
193
194
    /**
195
     * Compile a select query into SQL.
196
     *
197
     * @param IlluminateQueryBuilder $query
198
     * @return string
199
     */
200 371
    public function compileSelect(IlluminateQueryBuilder $query)
201
    {
202
        assert($query instanceof Builder);
203
204
        // If the query does not have any columns set, we'll set the columns to the
205
        // * character to just get all of the columns from the database. Then we
206
        // can build the query and concatenate all the pieces together as one.
207 371
        $original = $query->columns;
208
209 371
        if (empty($query->columns) || $query->unions !== null) {
210 45
            $query->columns = ['*'];
211
        }
212
213
        // To compile the query, we'll spin through each component of the query and
214
        // see if that component exists. If it does we'll just call the compiler
215
        // function for the component which is responsible for making the SQL.
216
217 371
        $aql = trim(
218 371
            $this->concatenate(
219 371
                $this->compileComponents($query),
220 371
            ),
221 371
        );
222
223 371
        $query->columns = $original;
224
225 371
        if ($query->unions) {
226 10
            return $this->compileUnions($query, $aql);
227
        }
228
229 371
        if ($query->groupVariables !== null) {
230 10
            $query->cleanGroupVariables();
231
        }
232
233 371
        return $aql;
234
    }
235
236
    /**
237
     * Compile the "from" portion of the query -> FOR in AQL.
238
     *
239
     * @param IlluminateQueryBuilder $query
240
     * @param Expression|string  $table
241
     *
242
     * @return string
243
     */
244 371
    protected function compileFrom(IlluminateQueryBuilder $query, $table)
245
    {
246
        assert($query instanceof Builder);
247
248
        // FIXME: wrapping/quoting
249 371
        $table = $this->prefixTable($this->getValue($table));
250
251 371
        $alias = $query->registerTableAlias($table);
252
253 371
        $aql = "FOR $alias IN $table";
254
255 371
        if (!empty($query->fromOptions)) {
256 1
            $aql .= $this->compileFromOptions($query->fromOptions);
257
        }
258
259 371
        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 371
    protected function compilePreIterationVariables(IlluminateQueryBuilder $query, array $variables): string
280
    {
281 371
        return $this->compileVariables($query, $variables);
282
    }
283
284
    /**
285
     * @param IlluminateQueryBuilder $query
286
     * @param array<string, mixed> $variables
287
     * @return string
288
     */
289 371
    protected function compilePostIterationVariables(IlluminateQueryBuilder $query, array $variables): string
290
    {
291 371
        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 371
    protected function compileVariables(IlluminateQueryBuilder $query, array $variables): string
303
    {
304 371
        $aql = '';
305
306 371
        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 371
        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 96
    protected function compileOrders(IlluminateQueryBuilder $query, $orders, $table = null)
326
    {
327 96
        if (!empty($orders)) {
328 96
            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 96
    protected function compileOrdersToArray(IlluminateQueryBuilder $query, $orders, $table = null)
344
    {
345 96
        return array_map(function ($order) use ($query, $table) {
346 96
            $key = 'column';
347 96
            if (array_key_exists('sql', $order)) {
348 2
                $key = 'sql';
349
            }
350
351 96
            if (!$order[$key] instanceof Expression) {
352 90
                $order[$key] = $this->normalizeColumn($query, $order[$key], $table);
353
            }
354
355 96
            if ($order[$key] instanceof Expression) {
356 6
                $order[$key] = $order[$key]->getValue($this);
357
            }
358
359 96
            return array_key_exists('direction', $order) ? $order[$key] . ' ' . $order['direction'] : $order[$key];
360 96
        }, $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 161
    protected function compileLimit(IlluminateQueryBuilder $query, $limit)
389
    {
390 161
        if ($this->offset !== null) {
391 10
            return "LIMIT " . (int) $this->offset . ", " . (int) $limit;
392
        }
393
394 151
        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 7
    public function compileSearch(IlluminateQueryBuilder $query, array $search)
417
    {
418 7
        if (isset($search['expression'])) {
419 1
            return (string) $this->getValue($search['expression']);
420
        }
421
422 6
        $predicates = [];
423 6
        foreach ($search['fields'] as $field) {
424 6
            $predicates[] = $this->normalizeColumn($query, $field)
425 6
                . ' IN TOKENS(' . $search['searchText'] . ', \'' . $search['analyzer'] . '\')';
426
        }
427
428 6
        return 'SEARCH ANALYZER('
429 6
            . implode(' OR ', $predicates)
430 6
            . ', \'' . $search['analyzer'] . '\')';
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 372
    public function getValue($expression)
440
    {
441 372
        if ($expression instanceof Expression) {
442 2
            return $expression->getValue($this);
443
        }
444
445 372
        return $expression;
446
    }
447
448
    /**
449
     * Get the grammar specific bit operators.
450
     *
451
     * @return array<string>
452
     */
453 226
    public function getBitwiseOperators()
454
    {
455 226
        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 35
    public function prepareBindingsForDelete(array $bindings)
465
    {
466 35
        return Arr::collapse(
467 35
            Arr::except($bindings, 'select'),
468 35
        );
469
    }
470
471
    /**
472
     * Determine if the given value is a raw expression.
473
     *
474
     * @param  mixed  $value
475
     * @return bool
476
     */
477 242
    public function isExpression($value)
478
    {
479 242
        return $value instanceof Expression;
480
    }
481
}
482