Passed
Push — next ( 9707a2...aa8376 )
by Bas
03:34
created

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