Failed Conditions
Branch master (1fae15)
by Bas
08:59
created

Grammar::compileInsert()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 22
rs 9.568
c 0
b 0
f 0
cc 3
nc 4
nop 2
1
<?php
2
3
namespace LaravelFreelancerNL\Aranguent\Query;
4
5
use Illuminate\Support\Str;
6
use Illuminate\Support\Traits\Macroable;
7
use LaravelFreelancerNL\FluentAQL\Exceptions\BindException as BindException;
8
use LaravelFreelancerNL\FluentAQL\Expressions\FunctionExpression;
9
use LaravelFreelancerNL\FluentAQL\Grammar as FluentAqlGrammar;
10
11
/*
12
 * Provides AQL syntax functions
13
 */
14
15
class Grammar extends FluentAqlGrammar
16
{
17
    use Macroable;
18
19
    public $name;
20
21
    /**
22
     * The grammar table prefix.
23
     *
24
     * @var string
25
     */
26
    protected $tablePrefix = '';
27
28
    /**
29
     * The grammar table prefix.
30
     *
31
     * @var null|int
32
     */
33
    protected $offset = null;
34
35
    /**
36
     * The components that make up a select clause.
37
     *
38
     * @var array
39
     */
40
    protected $selectComponents = [
41
        'from',
42
        'joins',
43
        'wheres',
44
        'groups',
45
        'aggregate',
46
        'havings',
47
        'orders',
48
        'offset',
49
        'limit',
50
        'columns',
51
    ];
52
53
    protected $operatorTranslations = [
54
        '=' => '==',
55
        '<>' => '!=',
56
        '<=>' => '==',
57
        'rlike' => '=~',
58
        'not rlike' => '!~',
59
        'regexp' => '=~',
60
        'not regexp' => '!~',
61
    ];
62
63
    /**
64
     * Get the format for database stored dates.
65
     *
66
     * @return string
67
     */
68
    public function getDateFormat()
69
    {
70
        return 'Y-m-d\TH:i:s.v\Z';
71
    }
72
73
    /**
74
     * Get the grammar specific operators.
75
     *
76
     * @return array
77
     */
78
    public function getOperators()
79
    {
80
        return $this->comparisonOperators;
81
    }
82
83
    /**
84
     * @param Builder $builder
85
     * @param $table
86
     * @param string $postfix
87
     * @return mixed
88
     */
89
    protected function generateTableAlias($builder, $table, $postfix = 'Doc')
90
    {
91
        $builder->registerAlias($table, Str::singular($table).$postfix);
92
93
        return $builder;
94
    }
95
96
    protected function prefixTable($table)
97
    {
98
        return $this->tablePrefix.$table;
99
    }
100
101
    /**
102
     * Compile an insert statement into AQL.
103
     *
104
     * @param Builder $builder
105
     * @param array $values
106
     * @return Builder
107
     * @throws BindException
108
     */
109
    public function compileInsert(Builder $builder, array $values)
110
    {
111
        if (! is_array(reset($values))) {
112
            $values = [$values];
113
        }
114
115
        $table = $this->prefixTable($builder->from);
116
117
        if (empty($values)) {
118
            $builder->aqb = $builder->aqb->insert('{}', $table)->get();
119
120
            return $builder;
121
        }
122
123
        $builder->aqb = $builder->aqb->let('docs', $builder->aqb->bind($values))
124
            ->for('doc', 'docs')
125
            ->insert('doc', $table)
126
            ->return('NEW._key')
127
            ->get();
128
129
        return $builder;
130
    }
131
132
    /**
133
     * Compile an insert and get ID statement into SQL.
134
     *
135
     * @param Builder $builder
136
     * @param array $values
137
     * @return Builder
138
     * @throws BindException
139
     */
140
    public function compileInsertGetId(Builder $builder, $values)
141
    {
142
        return $this->compileInsert($builder, $values);
143
    }
144
145
    /**
146
     * Compile a select query into AQL.
147
     *
148
     * @param  Builder  $builder
149
     * @return Builder
150
     */
151
    public function compileSelect(Builder $builder)
152
    {
153
//        if ($builder->unions && $builder->aggregate) {
154
//            return $this->compileUnionAggregate($builder);
155
//        }
156
157
        // To compile the query, we'll spin through each component of the query and
158
        // see if that component exists. If it does we'll just call the compiler
159
        // function for the component which is responsible for making the SQL.
160
161
        $builder = $this->compileComponents($builder);
162
163
//        if ($builder->unions) {
164
//            $sql = $this->wrapUnion($sql).' '.$this->compileUnions($builder);
165
//        }
166
167
        $builder->aqb = $builder->aqb->get();
168
169
        return $builder;
170
    }
171
172
    /**
173
     * Compile the components necessary for a select clause.
174
     *
175
     * @param Builder $builder
176
     * @return Builder
177
     */
178
    protected function compileComponents(Builder $builder)
179
    {
180
        foreach ($this->selectComponents as $component) {
181
            // To compile the query, we'll spin through each component of the query and
182
            // see if that component exists. If it does we'll just call the compiler
183
            // function for the component which is responsible for making the SQL.
184
185
            if (isset($builder->$component) && ! is_null($builder->$component)) {
186
                $method = 'compile'.ucfirst($component);
187
188
                $builder = $this->$method($builder, $builder->$component);
189
            }
190
        }
191
192
        return $builder;
193
    }
194
195
    /**
196
     * Compile the "from" portion of the query -> FOR in AQL.
197
     *
198
     * @param Builder $builder
199
     * @param string $table
200
     * @return Builder
201
     */
202
    protected function compileFrom(Builder $builder, $table)
203
    {
204
        $table = $this->prefixTable($table);
205
        $builder = $this->generateTableAlias($builder, $table);
206
        $tableAlias = $builder->getAlias($table);
207
208
        $builder->aqb = $builder->aqb->for($tableAlias, $table);
209
210
        return $builder;
211
    }
212
213
    /**
214
     * Compile the "where" portions of the query.
215
     *
216
     * @param Builder $builder
217
     * @return Builder
218
     */
219
    protected function compileWheres(Builder $builder)
220
    {
221
        // Each type of where clauses has its own compiler function which is responsible
222
        // for actually creating the where clauses SQL. This helps keep the code nice
223
        // and maintainable since each clause has a very small method that it uses.
224
        if (is_null($builder->wheres)) {
225
            return $builder;
226
        }
227
228
        if (count($predicates = $this->compileWheresToArray($builder)) > 0) {
229
            $builder->aqb = $builder->aqb->filter($predicates);
0 ignored issues
show
Documentation introduced by
$predicates is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
230
231
            return $builder;
232
        }
233
234
        return $builder;
235
    }
236
237
    /**
238
     * Get an array of all the where clauses for the query.
239
     *
240
     * @param  \Illuminate\Database\Query\Builder  $builder
241
     * @return array
242
     */
243
    protected function compileWheresToArray($builder)
244
    {
245
        $result = collect($builder->wheres)->map(function ($where) use ($builder) {
246
            // ArangoDB uses a double '=' for comparison
247
            if ($where['operator'] == '=') {
248
                $where['operator'] = '==';
249
            }
250
            $where['operator'] = $this->translateOperator($where['operator']);
251
252
            //Prefix table alias on the column
253
            $where['column'] = $this->prefixAlias($builder, $builder->from, $where['column']);
0 ignored issues
show
Compatibility introduced by
$builder of type object<Illuminate\Database\Query\Builder> is not a sub-type of object<LaravelFreelancer...ranguent\Query\Builder>. It seems like you assume a child class of the class Illuminate\Database\Query\Builder to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
254
255
            return [
256
                $where['column'],
257
                $where['operator'],
258
                $where['value'],
259
                $where['boolean'],
260
            ];
261
        })->all();
262
263
        return $result;
264
    }
265
266
    /**
267
     * Compile an aggregated select clause.
268
     *
269
     * @param  Builder  $builder
270
     * @param  array  $aggregate
271
     * @return Builder
272
     */
273
    protected function compileAggregate(Builder $builder, $aggregate)
274
    {
275
        $method = 'compile'.ucfirst($aggregate['function']);
276
277
        return $this->$method($builder, $aggregate);
278
    }
279
280
    /**
281
     * Compile AQL for count aggregate.
282
     * @param Builder $builder
283
     * @param $aggregate
284
     * @return Builder
285
     */
286
    protected function compileCount(Builder $builder, $aggregate)
0 ignored issues
show
Unused Code introduced by
The parameter $aggregate is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
287
    {
288
        $builder->aqb = $builder->aqb->collect()->withCount('aggregateResult');
289
290
        return $builder;
291
    }
292
293
    /**
294
     * Compile AQL for max aggregate.
295
     *
296
     * @param Builder $builder
297
     * @param $aggregate
298
     * @return Builder
299
     */
300
    protected function compileMax(Builder $builder, $aggregate)
301
    {
302
        $column = $this->prefixAlias($builder, $builder->from, $aggregate['columns'][0]);
303
304
        $builder->aqb = $builder->aqb->collect()->aggregate('aggregateResult', $builder->aqb->max($column));
305
306
        return $builder;
307
    }
308
309
    /**
310
     * Compile AQL for min aggregate.
311
     *
312
     * @param Builder $builder
313
     * @param $aggregate
314
     * @return Builder
315
     */
316
    protected function compileMin(Builder $builder, $aggregate)
317
    {
318
        $column = $this->prefixAlias($builder, $builder->from, $aggregate['columns'][0]);
319
320
        $builder->aqb = $builder->aqb->collect()->aggregate('aggregateResult', $builder->aqb->min($column));
321
322
        return $builder;
323
    }
324
325
    /**
326
     * Compile AQL for average aggregate.
327
     *
328
     * @param Builder $builder
329
     * @param $aggregate
330
     * @return Builder
331
     */
332
    protected function compileAvg(Builder $builder, $aggregate)
333
    {
334
        $column = $this->prefixAlias($builder, $builder->from, $aggregate['columns'][0]);
335
336
        $builder->aqb = $builder->aqb->collect()->aggregate('aggregateResult', $builder->aqb->average($column));
337
338
        return $builder;
339
    }
340
341
    /**
342
     * Compile AQL for sum aggregate.
343
     *
344
     * @param Builder $builder
345
     * @param $aggregate
346
     * @return Builder
347
     */
348
    protected function compileSum(Builder $builder, $aggregate)
349
    {
350
        $column = $this->prefixAlias($builder, $builder->from, $aggregate['columns'][0]);
351
352
        $builder->aqb = $builder->aqb->collect()->aggregate('aggregateResult', $builder->aqb->sum($column));
353
354
        return $builder;
355
    }
356
357
    /**
358
     * Compile the "order by" portions of the query.
359
     *
360
     * @param Builder $builder
361
     * @param array $orders
362
     * @return Builder
363
     */
364
    protected function compileOrders(Builder $builder, $orders)
365
    {
366
        if (! empty($orders)) {
367
            $builder->aqb = $builder->aqb->sort($this->compileOrdersToArray($builder, $orders));
368
369
            return $builder;
370
        }
371
372
        return $builder;
373
    }
374
375
    /**
376
     * Compile the query orders to an array.
377
     *
378
     * @param  Builder  $builder
379
     * @param  array  $orders
380
     * @return array
381
     */
382
    protected function compileOrdersToArray(Builder $builder, $orders)
383
    {
384
        return array_map(function ($order) use ($builder) {
385
            if (! isset($order['type']) || $order['type'] != 'Raw') {
386
                $order['column'] = $this->prefixAlias($builder, $builder->from, $order['column']);
387
            }
388
            unset($order['type']);
389
390
            return array_values($order);
391
        }, $orders);
392
    }
393
394
    /**
395
     * Compile the "offset" portions of the query.
396
     * We are handling this first by saving the offset which will be used by the FluentAQL's limit function.
397
     *
398
     * @param Builder $builder
399
     * @param int $offset
400
     * @return Builder
401
     */
402
    protected function compileOffset(Builder $builder, $offset)
403
    {
404
        $this->offset = (int) $offset;
405
406
        return $builder;
407
    }
408
409
    /**
410
     * Compile the "limit" portions of the query.
411
     *
412
     * @param Builder $builder
413
     * @param int $limit
414
     * @return Builder
415
     */
416
    protected function compileLimit(Builder $builder, $limit)
417
    {
418
        if ($this->offset !== null) {
419
            $builder->aqb = $builder->aqb->limit((int) $this->offset, (int) $limit);
420
421
            return $builder;
422
        }
423
        $builder->aqb = $builder->aqb->limit((int) $limit);
424
425
        return $builder;
426
    }
427
428
    /**
429
     * Compile the "RETURN" portion of the query.
430
     *
431
     * @param Builder $builder
432
     * @param array $columns
433
     * @return Builder
434
     */
435
    protected function compileColumns(Builder $builder, array $columns) : Builder
436
    {
437
        // If the query is actually performing an aggregating select, we will let that
438
        // compiler handle the building of the select clauses, as it will need some
439
        // more syntax that is best handled by that function to keep things neat.
440
//        if (! is_null($builder->aggregate)) {
441
//            return;
442
//        }
443
444
        $values = [];
445
446
        $doc = $builder->getAlias($builder->from);
447
        foreach ($columns as $column) {
448
            if ($column != null && $column != '*') {
449
                $values[$column] = $doc.'.'.$column;
450
            }
451
        }
452
        if ($builder->aggregate !== null) {
453
            $values = 'aggregateResult';
454
        }
455
        if (empty($values)) {
456
            $values = $doc;
457
        }
458
459
        $builder->aqb = $builder->aqb->return($values, (bool) $builder->distinct);
460
461
        return $builder;
462
    }
463
464
    /**
465
     * Compile an update statement into SQL.
466
     *
467
     * @param Builder $builder
468
     * @param array $values
469
     * @return Builder
470
     */
471
    public function compileUpdate(Builder $builder, array $values)
472
    {
473
        $table = $this->prefixTable($builder->from);
474
        $builder = $this->generateTableAlias($builder, $table);
475
        $tableAlias = $builder->getAlias($table);
476
        $builder->aqb = $builder->aqb->for($tableAlias, $table);
477
478
        //Fixme: joins?
479
        $builder = $this->compileWheres($builder);
480
481
        $builder->aqb = $builder->aqb->update($tableAlias, $values, $table)->get();
482
483
        return $builder;
484
    }
485
486
    /**
487
     * Compile a delete statement into SQL.
488
     *
489
     * @param Builder $builder
490
     * @param null $_key
491
     * @return Builder
492
     */
493
    public function compileDelete(Builder $builder, $_key = null)
494
    {
495
        $table = $this->prefixTable($builder->from);
496
        $builder = $this->generateTableAlias($builder, $table);
497
        $tableAlias = $builder->getAlias($table);
498
499
        if (! is_null($_key)) {
500
            $builder->aqb = $builder->aqb->remove((string) $_key, $table)->get();
501
502
            return $builder;
503
        }
504
505
        $builder->aqb = $builder->aqb->for($tableAlias, $table);
506
507
        //Fixme: joins?
508
        $builder = $this->compileWheres($builder);
509
510
        $builder->aqb = $builder->aqb->remove($tableAlias, $table)->get();
511
512
        return $builder;
513
    }
514
515
    /**
516
     * Compile the random statement into SQL.
517
     *
518
     * @param Builder $builder
519
     * @return FunctionExpression;
0 ignored issues
show
Documentation introduced by
The doc-type FunctionExpression; could not be parsed: Expected "|" or "end of type", but got ";" at position 18. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
520
     */
521
    public function compileRandom(Builder $builder)
522
    {
523
        return $builder->aqb->rand();
524
    }
525
526
    /**
527
     * Translate sql operators to their AQL equivalent where possible.
528
     *
529
     * @param string $operator
530
     * @return mixed|string
531
     */
532
    private function translateOperator(string $operator)
533
    {
534
        if (isset($this->operatorTranslations[strtolower($operator)])) {
535
            $operator = $this->operatorTranslations[$operator];
536
        }
537
538
        return $operator;
539
    }
540
541
    /**
542
     * @param Builder $builder
543
     * @param string $target
544
     * @param string $value
545
     * @return Builder
546
     */
547
    protected function prefixAlias(Builder $builder, string $target, string $value) : string
548
    {
549
        return $builder->getAlias($target).'.'.$value;
550
    }
551
}
552