Completed
Push — master ( 3ea209...a94746 )
by Bas
04:04
created

Grammar::compileAvg()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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

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

202
            $builder->aqb = $builder->aqb->filter(/** @scrutinizer ignore-type */ $predicates);
Loading history...
203
            return $builder;
204
        }
205
206
        return $builder;
207
    }
208
209
    /**
210
     * Get an array of all the where clauses for the query.
211
     *
212
     * @param  \Illuminate\Database\Query\Builder  $builder
213
     * @return array
214
     */
215
    protected function compileWheresToArray($builder)
216
    {
217
        $result = collect($builder->wheres)->map(function ($where) use ($builder) {
218
            // ArangoDB uses a double '=' for comparison
219
            if ($where['operator'] == '=') {
220
                $where['operator'] = '==';
221
            }
222
            //Prefix table alias on the column
223
            $where['column'] = $this->prefixAlias($builder, $builder->from, $where['column'] );
224
225
            return [
226
                $where['column'],
227
                $where['operator'],
228
                $where['value'],
229
                $where['boolean']
230
            ];
231
        })->all();
232
        return $result;
233
    }
234
235
    /**
236
     * Compile an aggregated select clause.
237
     *
238
     * @param  Builder  $builder
239
     * @param  array  $aggregate
240
     * @return Builder
241
     */
242
    protected function compileAggregate(Builder $builder, $aggregate)
243
    {
244
        $method = 'compile'.ucfirst($aggregate['function']);
245
        return $this->$method($builder, $aggregate);
246
247
    }
248
249
    /**
250
     * Compile AQL for count aggregate
251
     * @param Builder $builder
252
     * @param $aggregate
253
     * @return Builder
254
     */
255
    protected function compileCount(Builder $builder, $aggregate)
256
    {
257
        $builder->aqb = $builder->aqb->collect()->withCount('aggregateResult');
258
        return $builder;
259
    }
260
261
    /**
262
     * Compile AQL for max aggregate
263
     *
264
     * @param Builder $builder
265
     * @param $aggregate
266
     * @return Builder
267
     */
268
    protected function compileMax(Builder $builder, $aggregate)
269
    {
270
        $column = $this->prefixAlias($builder, $builder->from, $aggregate['columns'][0]);
271
272
        $builder->aqb = $builder->aqb->collect()->aggregate('aggregateResult', $builder->aqb->max($column));
273
        return $builder;
274
    }
275
276
    /**
277
     * Compile AQL for min aggregate
278
     *
279
     * @param Builder $builder
280
     * @param $aggregate
281
     * @return Builder
282
     */
283
    protected function compileMin(Builder $builder, $aggregate)
284
    {
285
        $column = $this->prefixAlias($builder, $builder->from, $aggregate['columns'][0]);
286
287
        $builder->aqb = $builder->aqb->collect()->aggregate('aggregateResult', $builder->aqb->min($column));
288
        return $builder;
289
    }
290
291
    /**
292
     * Compile AQL for average aggregate
293
     *
294
     * @param Builder $builder
295
     * @param $aggregate
296
     * @return Builder
297
     */
298
    protected function compileAvg(Builder $builder, $aggregate)
299
    {
300
        $column = $this->prefixAlias($builder, $builder->from, $aggregate['columns'][0]);
301
302
        $builder->aqb = $builder->aqb->collect()->aggregate('aggregateResult', $builder->aqb->average($column));
303
        return $builder;
304
    }
305
306
    /**
307
     * Compile AQL for sum aggregate
308
     *
309
     * @param Builder $builder
310
     * @param $aggregate
311
     * @return Builder
312
     */
313
    protected function compileSum(Builder $builder, $aggregate)
314
    {
315
        $column = $this->prefixAlias($builder, $builder->from, $aggregate['columns'][0]);
316
317
        $builder->aqb = $builder->aqb->collect()->aggregate('aggregateResult', $builder->aqb->sum($column));
318
        return $builder;
319
    }
320
321
    /**
322
     * Compile the "order by" portions of the query.
323
     *
324
     * @param Builder $builder
325
     * @param array $orders
326
     * @return Builder
327
     */
328
    protected function compileOrders(Builder $builder, $orders)
329
    {
330
        if (! empty($orders)) {
331
            $builder->aqb = $builder->aqb->sort($this->compileOrdersToArray($builder, $orders));
332
            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
     * @return array
344
     */
345
    protected function compileOrdersToArray(Builder $builder, $orders)
346
    {
347
        return array_map(function ($order) {
348
            return $order['sql'] ?? $this->prefixTable($order['column']).' '.$order['direction'];
349
        }, $orders);
350
    }
351
352
    /**
353
     * Compile the "offset" portions of the query.
354
     * We are handling this first by saving the offset which will be used by the FluentAQL's limit function
355
     *
356
     * @param Builder $builder
357
     * @param int $offset
358
     * @return Builder
359
     */
360
    protected function compileOffset(Builder $builder, $offset)
361
    {
362
        $this->offset = (int) $offset;
363
364
        return $builder;
365
    }
366
367
    /**
368
     * Compile the "limit" portions of the query.
369
     *
370
     * @param Builder $builder
371
     * @param int $limit
372
     * @return Builder
373
     */
374
    protected function compileLimit(Builder $builder, $limit)
375
    {
376
        if ($this->offset !== null) {
377
            $builder->aqb = $builder->aqb->limit((int)$this->offset, (int)$limit);
378
            return $builder;
379
        }
380
        $builder->aqb = $builder->aqb->limit((int) $limit);
381
        return $builder;
382
    }
383
384
385
    /**
386
     * Compile the "RETURN" portion of the query.
387
     *
388
     * @param Builder $builder
389
     * @param array $columns
390
     * @return Builder
391
     */
392
    protected function compileColumns(Builder $builder, array $columns) : Builder
393
    {
394
        // If the query is actually performing an aggregating select, we will let that
395
        // compiler handle the building of the select clauses, as it will need some
396
        // more syntax that is best handled by that function to keep things neat.
397
//        if (! is_null($builder->aggregate)) {
398
//            return;
399
//        }
400
401
        $values = [];
402
403
        $doc = $builder->getAlias($builder->from);
404
        foreach ($columns as $column) {
405
            if ($column != null && $column != '*') {
406
                $values[$column] = $doc.'.'.$column;
407
            }
408
        }
409
        if ($builder->aggregate !== null) {
410
            $values = 'aggregateResult';
411
        }
412
        if (empty($values)) {
413
            $values = $doc;
414
        }
415
416
417
        $builder->aqb = $builder->aqb->return($values, (boolean) $builder->distinct);
418
        return $builder;
419
    }
420
421
    /**
422
     * Compile an update statement into SQL.
423
     *
424
     * @param Builder $builder
425
     * @param array $values
426
     * @return Builder
427
     */
428
    public function compileUpdate(Builder $builder, array $values)
429
    {
430
        $table = $this->prefixTable($builder->from);
431
        $builder = $this->generateTableAlias($builder, $table);
432
        $tableAlias = $builder->getAlias($table);
433
        $builder->aqb = $builder->aqb->for($tableAlias, $table);
434
435
        //Fixme: joins?
436
        $builder = $this->compileWheres($builder);
437
438
        $builder->aqb = $builder->aqb->update($tableAlias, $values, $table)->get();
439
440
        return $builder;
441
    }
442
443
    /**
444
     * Compile a delete statement into SQL.
445
     *
446
     * @param Builder $builder
447
     * @param null $_key
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $_key is correct as it would always require null to be passed?
Loading history...
448
     * @return Builder
449
     */
450
    public function compileDelete(Builder $builder, $_key = null)
451
    {
452
        $table = $this->prefixTable($builder->from);
453
        $builder = $this->generateTableAlias($builder, $table);
454
        $tableAlias = $builder->getAlias($table);
455
456
        if (! is_null($_key)) {
0 ignored issues
show
introduced by
The condition is_null($_key) is always true.
Loading history...
457
            $builder->aqb = $builder->aqb->remove((string) $_key, $table)->get();
458
            return $builder;
459
        }
460
461
        $builder->aqb = $builder->aqb->for($tableAlias, $table);
462
463
        //Fixme: joins?
464
        $builder = $this->compileWheres($builder);
465
466
        $builder->aqb = $builder->aqb->remove($tableAlias, $table)->get();
467
468
        return $builder;
469
    }
470
471
    /**
472
     * @param Builder $builder
473
     * @param string $target
474
     * @param string $value
475
     * @return Builder
476
     */
477
    protected function prefixAlias(Builder $builder, string $target, string $value) : string
478
    {
479
        return $builder->getAlias($target).'.'.$value;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $builder->getAlias($target) . '.' . $value returns the type string which is incompatible with the documented return type LaravelFreelancerNL\Aranguent\Query\Builder.
Loading history...
480
    }
481
482
483
    /**
484
     * Compile the random statement into SQL.
485
     *
486
     * @param Builder $builder
487
     * @return string
488
     */
489
    public function compileRandom(Builder $builder)
490
    {
491
        return (string) $builder->aqb->rand();
492
    }
493
}
494