Passed
Push — next ( 4a2827...9707a2 )
by Bas
07:22
created

Builder::select()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 5
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 Closure;
6
use Illuminate\Database\ConnectionInterface;
7
use Illuminate\Database\Query\Builder as IlluminateQueryBuilder;
8
use Illuminate\Database\Query\Expression;
9
use Illuminate\Support\Arr;
10
use Illuminate\Support\Collection;
11
use InvalidArgumentException;
12
use LaravelFreelancerNL\Aranguent\Connection;
13
use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsJoinClauses;
14
use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsSubqueries;
15
use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsWhereClauses;
16
use LaravelFreelancerNL\FluentAQL\Exceptions\BindException;
17
use LaravelFreelancerNL\FluentAQL\Expressions\ExpressionInterface as ExpressionInterface;
18
use LaravelFreelancerNL\FluentAQL\QueryBuilder;
19
20
class Builder extends IlluminateQueryBuilder
21
{
22
    use BuildsJoinClauses;
23
    use BuildsSubqueries;
24
    use BuildsWhereClauses;
25
26
    /**
27
     * @var Grammar
28
     */
29
    public $grammar;
30
31
    /**
32
     * @var Connection
33
     */
34
    public $connection;
35
36
    /**
37
     * @var QueryBuilder
38
     */
39
    public $aqb;
40
41
    /**
42
     * The query variables that should be set.
43
     *
44
     * @var array<mixed>
45
     */
46
    public array $variables = [];
47
48
    /**
49
     * @override
50
     * Create a new query builder instance.
51
     *
52
     * @param  ConnectionInterface  $connection
53
     * @param  Grammar|null  $grammar
54
     * @param  Processor|null  $processor
55
     * @param  QueryBuilder|null  $aqb
56
     */
57
    public function __construct(
58 108
        ConnectionInterface $connection,
59
        Grammar $grammar = null,
60
        Processor $processor = null,
61
        QueryBuilder $aqb = null
62
    ) {
63
        $this->connection = $connection;
0 ignored issues
show
Documentation Bug introduced by
$connection is of type Illuminate\Database\ConnectionInterface, but the property $connection was declared to be of type LaravelFreelancerNL\Aranguent\Connection. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
64 108
        $this->grammar = $grammar ?: $connection->getQueryGrammar();
65 108
        $this->processor = $processor ?: $connection->getPostProcessor();
66 108
        if (!$aqb instanceof QueryBuilder) {
67 108
            $aqb = new QueryBuilder();
68 108
        }
69
        $this->aqb = $aqb;
70 108
    }
71 108
72
    /**
73
     * Delete a record from the database.
74
     *
75
     * @param mixed $id
76
     *
77
     * @return int
78
     */
79
    public function delete($id = null)
80 6
    {
81
        $this->aqb = new QueryBuilder();
82 6
        $this->grammar->compileDelete($this, $id)->setAql();
83 6
        return $this->connection->delete($this->aqb);
84 6
    }
85
86
    /**
87
     * Set the table which the query is targeting.
88
     *
89
     * @param  \Closure|IlluminateQueryBuilder|string  $table
90
     * @param  string|null  $as
91
     * @return IlluminateQueryBuilder
92
     */
93
    public function from($table, $as = null)
94 108
    {
95
        if ($this->isQueryable($table)) {
96 108
            return $this->fromSub($table, $as);
97
        }
98
        $this->grammar->registerTableAlias($table, $as);
0 ignored issues
show
Bug introduced by
It seems like $table can also be of type Closure and Illuminate\Database\Query\Builder; however, parameter $table of LaravelFreelancerNL\Aran...r::registerTableAlias() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

98
        $this->grammar->registerTableAlias(/** @scrutinizer ignore-type */ $table, $as);
Loading history...
99 108
100
        $this->from = $table;
0 ignored issues
show
Documentation Bug introduced by
It seems like $table can also be of type Closure or Illuminate\Database\Query\Builder. However, the property $from is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
101 108
102
        return $this;
103 108
    }
104
105
    /**
106
     * Run the query as a "select" statement against the connection.
107
     *
108
     * @return array
109
     */
110
    protected function runSelect()
111 75
    {
112
        $this->aqb = new QueryBuilder();
113 75
        $this->grammar->compileSelect($this)->setAql();
114 75
        $results = $this->connection->select($this->aqb);
115 75
        $this->aqb = new QueryBuilder();
116 74
        return $results;
117 74
    }
118
119
    /**
120
     * Run a pagination count query.
121
     *
122
     * @param array $columns
123
     *
124
     * @return array
125
     */
126
    protected function runPaginationCountQuery($columns = ['*'])
127 1
    {
128
        $without = $this->unions ? ['orders', 'limit', 'offset'] : ['columns', 'orders', 'limit', 'offset'];
129 1
130
        $closeResults = $this->cloneWithout($without)
131 1
            ->cloneWithoutBindings($this->unions ? ['order'] : ['select', 'order'])
132 1
            ->setAggregate('count', $this->withoutSelectAliases($columns))
133 1
            ->get()->all();
134 1
135
        $this->aqb = new QueryBuilder();
136 1
137
        return $closeResults;
138 1
    }
139
140
    /**
141
     * Set the columns to be selected.
142
     *
143
     * @param  array|mixed  $columns
144
     * @return IlluminateQueryBuilder
145
     */
146
    public function select($columns = ['*'])
147 43
    {
148
        $this->columns = [];
149 43
        $this->bindings['select'] = [];
150 43
        $columns = is_array($columns) ? $columns : func_get_args();
151 43
152
        $this->addColumns($columns);
153 43
154
        return $this;
155 43
    }
156
157
    /**
158
     * Add a new select column to the query.
159
     *
160
     * @param  array|mixed  $column
161
     * @return $this
162
     */
163
    public function addSelect($column)
164 11
    {
165
        $columns = is_array($column) ? $column : func_get_args();
166 11
167
        $this->addColumns($columns);
168 11
169
        return $this;
170 11
    }
171
172
    /**
173
     * @param array<mixed> $columns
174
     */
175
    protected function addColumns(array $columns): void
176 54
    {
177
        foreach ($columns as $as => $column) {
178 54
            if (is_string($as) && $this->isQueryable($column)) {
179 54
                if (is_null($this->columns)) {
180
                    $this->select($this->from . '.*');
181
                }
182
183
                $this->selectSub($column, $as);
184
185
                continue;
186
            }
187
188
            if (! is_string($as) || ! $this->isQueryable($column)) {
189 54
                $this->columns[$as] = $column;
190 54
191
                continue;
192 54
            }
193
194
            $this->columns[] = $column;
195
        }
196
    }
197 54
198
    /**
199
     * Get the SQL representation of the query.
200
     *
201
     * @return string
202
     */
203
    public function toSql()
204 29
    {
205
        $this->grammar->compileSelect($this)->setAql();
206 29
        return $this->aqb->query;
207 29
    }
208
209
    /**
210
     * Insert a new record into the database.
211
     *
212
     * @param array $values
213
     *
214
     * @throws BindException
215
     *
216
     * @return bool
217
     */
218
    public function insert(array $values): bool
219 80
    {
220
        $this->grammar->compileInsert($this, $values)->setAql();
221 80
        $results = $this->getConnection()->insert($this->aqb);
222 80
        $this->aqb = new QueryBuilder();
223 80
        return $results;
224 80
    }
225
226
    /**
227
     * Insert a new record and get the value of the primary key.
228
     *
229
     * @param array<mixed> $values
230
     */
231
    public function insertGetId(array $values, $sequence = null)
232
    {
233
        $this->grammar->compileInsertGetId($this, $values, $sequence)->setAql();
234
        $response = $this->getConnection()->execute($this->aqb);
235
        $this->aqb = new QueryBuilder();
236
237 10
        return (is_array($response)) ? end($response) : $response;
238
    }
239 10
240 10
    /**
241 10
     * Insert a new record into the database.
242
     *
243 10
     * @param array $values
244
     *
245
     * @throws BindException
246
     *
247
     * @return bool
248
     */
249
    public function insertOrIgnore(array $values): bool
250
    {
251
        $this->grammar->compileInsertOrIgnore($this, $values)->setAql();
252
        $results = $this->getConnection()->insert($this->aqb);
253 74
        $this->aqb = new QueryBuilder();
254
255 74
        return $results;
256 74
    }
257 74
258
259
    /**
260
     * Execute the query as a "select" statement.
261
     *
262
     * @param array|string $columns
263
     *
264
     * @return Collection
265 10
     */
266
    public function get($columns = ['*'])
267 10
    {
268
        return collect($this->onceWithColumns(Arr::wrap($columns), function () {
269
            return $this->runSelect();
270 108
        }));
271
    }
272 108
273
    /**
274 108
     * Get the current query value bindings in a flattened array.
275
     *
276
     * @return array
277
     */
278
    public function getBindings()
279
    {
280
        return $this->aqb->binds;
281
    }
282
283
    protected function setAql()
284 16
    {
285
        $this->aqb = $this->aqb->get();
286 16
287
        return $this;
288 16
    }
289 16
290 16
    /**
291 16
     * Update a record in the database.
292
     *
293
     * @param array $values
294
     *
295
     * @return int
296
     */
297
    public function update(array $values)
298
    {
299
        $this->aqb = new QueryBuilder();
300
301
        $this->grammar->compileUpdate($this, $values)->setAql();
302
        $results = $this->connection->update($this->aqb);
303
        $this->aqb = new QueryBuilder();
304 6
        return $results;
305
    }
306 6
307 6
308 6
309
    /**
310 6
     * Execute an aggregate function on the database.
311
     *
312 6
     * @param string $function
313 6
     * @param array  $columns
314
     *
315
     * @return mixed
316
     */
317
    public function aggregate($function, $columns = ['*'])
318
    {
319
        $results = $this->cloneWithout($this->unions ? [] : ['columns'])
320
            ->setAggregate($function, $columns)
321
            ->get($columns);
322
323
        $this->aqb = new QueryBuilder();
324
325
        if (!$results->isEmpty()) {
326
            return array_change_key_case((array) $results[0])['aggregate'];
327 67
        }
328
329 67
        return false;
330 67
    }
331
332
333
    /**
334
     * Determine if the given operator is supported.
335
     *
336
     * @param string $operator
337
     *
338
     * @return bool
339
     */
340
    protected function invalidOperator($operator)
341
    {
342
        return !in_array(strtolower($operator), $this->operators, true) &&
343
            !isset($this->grammar->getOperators()[strtoupper($operator)]);
344 1
    }
345
346 1
347
    /**
348
     * Add an "order by" clause to the query.
349
     *
350
     * @param Closure|IlluminateQueryBuilder|string $column
351
     * @param string                                $direction
352
     *
353
     * @throws InvalidArgumentException
354
     *
355 1
     * @return $this
356 1
     */
357 1
    public function orderBy($column, $direction = 'asc')
358
    {
359
        if ($this->isQueryable($column)) {
360 1
            [$query, $bindings] = $this->createSub($column);
361
362
            //fixme: Remove binding when implementing subqueries
363
            $bindings = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $bindings is dead and can be removed.
Loading history...
364
365
            $column = new Expression('(' . $query . ')');
366
        }
367
368
        $this->{$this->unions ? 'unionOrders' : 'orders'}[] = [
369
            'column'    => $column,
370
            'direction' => $direction,
371 1
        ];
372
373 1
        return $this;
374
    }
375 1
376 1
    /**
377 1
     * Add a raw "order by" clause to the query.
378
     *
379 1
     * @param string|ExpressionInterface $aql
380
     * @param array                      $bindings
381
     *
382
     * @return $this
383
     */
384
    public function orderByRaw($aql, $bindings = [])
385
    {
386
        $bindings = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $bindings is dead and can be removed.
Loading history...
387
388
        $type = 'Raw';
389
        $column = $aql;
390 1
        $this->{$this->unions ? 'unionOrders' : 'orders'}[] = compact('type', 'column');
391
392
        return $this;
393 1
    }
394
395 1
396
    /**
397
     * Put the query's results in random order.
398
     *
399
     * @param string $seed
400
     *
401
     * @return $this
402
     */
403
    public function inRandomOrder($seed = '')
404
    {
405
        // ArangoDB's random function doesn't accept a seed.
406
        $seed = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $seed is dead and can be removed.
Loading history...
407
408
        return $this->orderByRaw($this->grammar->compileRandom($this));
409
    }
410
}
411