Failed Conditions
Push — refactor/improve-static-analys... ( 8da3ef...a7b39f )
by Bas
09:53
created

Builder::insert()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 4
c 0
b 0
f 0
dl 0
loc 6
ccs 1
cts 1
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace LaravelFreelancerNL\Aranguent\Query;
4
5
use Closure;
6
use Illuminate\Database\Query\Builder as IlluminateQueryBuilder;
7
use Illuminate\Database\Query\Expression;
8
use InvalidArgumentException;
9
use LaravelFreelancerNL\Aranguent\Connection;
10
use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsJoinClauses;
11
use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsSubqueries;
12
use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsWhereClauses;
13
use LaravelFreelancerNL\FluentAQL\Exceptions\BindException;
14
use LaravelFreelancerNL\FluentAQL\Expressions\ExpressionInterface as ExpressionInterface;
15
use LaravelFreelancerNL\FluentAQL\Expressions\FunctionExpression;
16
use LaravelFreelancerNL\FluentAQL\QueryBuilder;
17
18
class Builder extends IlluminateQueryBuilder
19
{
20
    use BuildsJoinClauses;
21
    use BuildsSubqueries;
22
    use BuildsWhereClauses;
23
24
    /**
25
     * @var QueryBuilder|FunctionExpression
26
     */
27
    public QueryBuilder|FunctionExpression $aqb;
28
29
    /**
30
     * @var Connection
31
     */
32
    public $connection;
33
34
    /**
35
     * @var Grammar
36
     */
37
    public $grammar;
38
39
    /**
40
     * The current query value bindings.
41
     *
42
     * @var null|array{predicates: array<mixed>, options: array<string, string>}
43
     */
44
    public ?array $search = null;
45
46
    /**
47
     * The query variables that should be set.
48
     *
49
     * @var array<mixed>
50
     */
51
    public $variables = [];
52
53
    /**
54
     * @override
55
     * Create a new query builder instance.
56
     */
57
    public function __construct(
58
        Connection $connection,
59
        Grammar $grammar = null,
60 162
        Processor $processor = null,
61
        QueryBuilder $aqb = null
62
    ) {
63
        $this->connection = $connection;
64
        $this->grammar = $grammar ?: $connection->getQueryGrammar();
65
        $this->processor = $processor ?: $connection->getPostProcessor();
66 162
        if (!$aqb instanceof QueryBuilder) {
67 162
            $aqb = new QueryBuilder();
68 162
        }
69 162
        $this->aqb = $aqb;
70 162
    }
71
72 162
    /**
73 162
     * Delete a record from the database.
74
     *
75
     * @param mixed $id
76
     *
77
     * @return int
78
     */
79
    public function delete($id = null)
80
    {
81
        $this->aqb = new QueryBuilder();
82 12
        $this->grammar->compileDelete($this, $id)->setAql();
83
        return $this->connection->delete($this->aqb);
84 12
    }
85 12
86 12
    /**
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
    {
95
        if ($this->isQueryable($table)) {
96 162
            return $this->fromSub($table, $as);
97
        }
98 162
        $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
        $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 162
102
        return $this;
103 162
    }
104
105 162
    /**
106
     * Run a pagination count query.
107
     *
108
     * @param array<mixed> $columns
109
     *
110
     * @return array<mixed>
111
     */
112
    protected function runPaginationCountQuery($columns = ['*'])
113 112
    {
114
        $without = $this->unions ? ['orders', 'limit', 'offset'] : ['columns', 'orders', 'limit', 'offset'];
115 112
116 112
        $closeResults = $this->cloneWithout($without)
117 112
            ->cloneWithoutBindings($this->unions ? ['order'] : ['select', 'order'])
118 111
            ->setAggregate('count', $this->withoutSelectAliases($columns))
119 111
            ->get()->all();
120
121
        return $closeResults;
122
    }
123
124
    /**
125
     * Set the columns to be selected.
126
     *
127
     * @param  array<mixed>|mixed  $columns
128
     */
129 3
    public function select($columns = ['*']): IlluminateQueryBuilder
130
    {
131 3
        $this->columns = [];
132
        $this->bindings['select'] = [];
133 3
134 3
        $columns = is_array($columns) ? $columns : func_get_args();
135 3
        foreach ($columns as $as => $column) {
136 3
            if (is_string($as) && $this->isQueryable($column)) {
137
                $this->selectSub($column, $as);
138 3
            } else {
139
                $this->addColumns($columns);
140
            }
141
        }
142
143
        return $this;
144
    }
145
146
    /**
147 53
     * Add a new select column to the query.
148
     *
149 53
     * @param  array|mixed  $column
150 53
     * @return $this
151 53
     */
152
    public function addSelect($column)
153 53
    {
154
        $columns = is_array($column) ? $column : func_get_args();
155 53
156
        $this->addColumns($columns);
157
158
        return $this;
159
    }
160
161
    /**
162
     * @param array<mixed> $columns
163
     */
164 11
    protected function addColumns(array $columns): void
165
    {
166 11
        foreach ($columns as $as => $column) {
167
            if (is_string($as) && $this->isQueryable($column)) {
168 11
                if (is_null($this->columns)) {
169
                    $this->select($this->from . '.*');
170 11
                }
171
172
                $this->selectSub($column, $as);
173
174
                continue;
175
            }
176 64
177
            if (is_string($as)) {
178 64
                $this->columns[$as] = $column;
179 64
180
                continue;
181
            }
182
183
            $this->columns[] = $column;
184
        }
185
    }
186
187
    /**
188
     * Insert a new record into the database.
189 64
     *
190 64
     * @param array<mixed> $values
191
     *
192 64
     * @throws BindException
193
     *
194
     * @return bool
195
     */
196
    public function insert(array $values): bool
197 64
    {
198
        $this->grammar->compileInsert($this, $values)->setAql();
199
        $results = $this->getConnection()->insert($this->aqb);
0 ignored issues
show
Bug introduced by
It seems like $this->aqb can also be of type LaravelFreelancerNL\Flue...ions\FunctionExpression; however, parameter $query of Illuminate\Database\Connection::insert() 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

199
        $results = $this->getConnection()->insert(/** @scrutinizer ignore-type */ $this->aqb);
Loading history...
200
        $this->aqb = new QueryBuilder();
201
        return $results;
202
    }
203
204 40
    /**
205
     * Insert a new record and get the value of the primary key.
206 40
     *
207 40
     * @param array<mixed> $values
208
     */
209
    public function insertGetId(array $values, $sequence = null)
210
    {
211
        $this->grammar->compileInsertGetId($this, $values, $sequence)->setAql();
212
        $response = $this->getConnection()->execute($this->aqb);
0 ignored issues
show
Bug introduced by
It seems like $this->aqb can also be of type LaravelFreelancerNL\Flue...ions\FunctionExpression; however, parameter $query of LaravelFreelancerNL\Aran...t\Connection::execute() does only seem to accept LaravelFreelancerNL\FluentAQL\QueryBuilder|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

212
        $response = $this->getConnection()->execute(/** @scrutinizer ignore-type */ $this->aqb);
Loading history...
213
        $this->aqb = new QueryBuilder();
214
215
        return (is_array($response)) ? end($response) : $response;
216
    }
217
218
    /**
219 44
     * Insert a new record into the database.
220
     *
221 44
     * @param array<mixed> $values
222 44
     *
223 44
     * @throws BindException
224 44
     *
225
     * @return bool
226
     */
227
    public function insertOrIgnore(array $values): bool
228
    {
229
        $this->grammar->compileInsertOrIgnore($this, $values)->setAql();
230
        $results = $this->getConnection()->insert($this->aqb);
0 ignored issues
show
Bug introduced by
It seems like $this->aqb can also be of type LaravelFreelancerNL\Flue...ions\FunctionExpression; however, parameter $query of Illuminate\Database\Connection::insert() 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

230
        $results = $this->getConnection()->insert(/** @scrutinizer ignore-type */ $this->aqb);
Loading history...
231
        $this->aqb = new QueryBuilder();
232 10
233
        return $results;
234 10
    }
235 10
236 10
    /**
237
    /**
238 10
     * Get the current query value bindings in a flattened array.
239
     *
240
     * @return array<mixed>
241
     */
242
    public function getBindings()
243
    {
244
        return $this->aqb->binds;
0 ignored issues
show
Bug introduced by
The property binds does not seem to exist on LaravelFreelancerNL\Flue...ions\FunctionExpression.
Loading history...
245
    }
246
247
    protected function setAql()
248
    {
249
        $this->aqb = $this->aqb->get();
0 ignored issues
show
Bug introduced by
The method get() does not exist on LaravelFreelancerNL\Flue...ions\FunctionExpression. ( Ignorable by Annotation )

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

249
        /** @scrutinizer ignore-call */ 
250
        $this->aqb = $this->aqb->get();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
250 98
251
        return $this;
252 98
    }
253 98
254 98
    /**
255
     * Update a record in the database.
256 98
     *
257
     * @param array<mixed> $values
258
     *
259
     * @return int
260
     */
261
    public function update(array $values)
262
    {
263
        $this->aqb = new QueryBuilder();
264
265 10
        $this->grammar->compileUpdate($this, $values)->setAql();
266
        $results = $this->connection->update($this->aqb);
267 10
        $this->aqb = new QueryBuilder();
268
        return $results;
269
    }
270 162
271
    /**
272 162
     * Execute an aggregate function on the database.
273
     *
274 162
     * @param string $function
275
     * @param array<mixed>  $columns
276
     *
277
     * @return mixed
278
     */
279
    public function aggregate($function, $columns = ['*'])
280
    {
281
        $results = $this->cloneWithout($this->unions ? [] : ['columns'])
282
            ->setAggregate($function, $columns)
283
            ->get($columns);
284 23
285
        $this->aqb = new QueryBuilder();
286 23
287
        if (!$results->isEmpty()) {
288 23
            return array_change_key_case((array) $results[0])['aggregate'];
289 23
        }
290 23
291 23
        return false;
292
    }
293
294
295
    /**
296
     * Determine if the given operator is supported.
297
     *
298
     * @param string $operator
299
     *
300
     * @return bool
301
     */
302 21
    protected function invalidOperator($operator)
303
    {
304 21
        return !in_array(strtolower($operator), $this->operators, true) &&
305 21
            !isset($this->grammar->getOperators()[strtoupper($operator)]);
306 21
    }
307
308 21
309
    /**
310 21
     * Add an "order by" clause to the query.
311 21
     *
312
     * @param Closure|IlluminateQueryBuilder|string $column
313
     * @param string                                $direction
314
     *
315
     * @throws InvalidArgumentException
316
     *
317
     * @return $this
318
     */
319
    public function orderBy($column, $direction = 'asc')
320
    {
321
        if ($this->isQueryable($column)) {
322
            [$query, $bindings] = $this->createSub($column);
323
324
            //fixme: Remove binding when implementing subqueries
325 88
            $bindings = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $bindings is dead and can be removed.
Loading history...
326
327 88
            $column = new Expression('(' . $query . ')');
328 88
        }
329
330
        $this->{$this->unions ? 'unionOrders' : 'orders'}[] = [
331
            'column'    => $column,
332
            'direction' => $direction,
333
        ];
334
335
        return $this;
336
    }
337
338
    /**
339
     * Add a raw "order by" clause to the query.
340
     *
341
     * @param string|ExpressionInterface $aql
342 2
     * @param array<mixed>                      $bindings
343
     *
344 2
     * @return $this
345
     */
346
    public function orderByRaw($aql, $bindings = [])
347
    {
348
        $bindings = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $bindings is dead and can be removed.
Loading history...
349
350
        $type = 'Raw';
351
        $column = $aql;
352
        $this->{$this->unions ? 'unionOrders' : 'orders'}[] = compact('type', 'column');
353 2
354 2
        return $this;
355 2
    }
356
357
358 2
    /**
359
     * Put the query's results in random order.
360
     *
361
     * @param string $seed
362
     *
363
     * @return $this
364
     */
365
    public function inRandomOrder($seed = '')
366
    {
367
        // ArangoDB's random function doesn't accept a seed.
368
        $seed = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $seed is dead and can be removed.
Loading history...
369 1
370
        return $this->orderByRaw($this->grammar->compileRandom($this));
0 ignored issues
show
Bug introduced by
$this of type LaravelFreelancerNL\Aranguent\Query\Builder is incompatible with the type string expected by parameter $seed of LaravelFreelancerNL\Aran...rammar::compileRandom(). ( Ignorable by Annotation )

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

370
        return $this->orderByRaw($this->grammar->compileRandom(/** @scrutinizer ignore-type */ $this));
Loading history...
371 1
    }
372
373 1
    /**
374 1
     * Search an ArangoSearch view.
375 1
     *
376
     * @param mixed $predicates
377 1
     * @param array|null $options
378
     * @return Builder
379
     */
380
    public function search(mixed $predicates, array $options = null): Builder
381
    {
382
        if ($predicates instanceof Closure) {
383
            $predicates = $predicates($this->aqb);
384
        }
385
386
        if (! is_array($predicates)) {
387
            $predicates = [$predicates];
388 1
        }
389
390
        $this->search = [
391 1
            'predicates' => $predicates,
392
            'options' => $options
393 1
        ];
394
395
        return $this;
396
    }
397
398
    /**
399
     * Create a new query instance for a sub-query.
400
     *
401
     * @return $this
402
     */
403 7
    protected function forSubQuery()
404
    {
405 7
        return $this->newQuery();
406 2
    }
407
408
    /**
409 7
     * Get the database connection instance.
410 4
     *
411
     * @return Connection
412
     */
413 7
    public function getConnection()
414 7
    {
415 7
        return $this->connection;
416
    }
417
}
418