Completed
Push — master ( 42aa60...7d653e )
by Bas
03:14
created

Builder::insert()   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 1
dl 0
loc 6
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 Illuminate\Support\Str;
12
use InvalidArgumentException;
13
use LaravelFreelancerNL\Aranguent\Connection;
14
use LaravelFreelancerNL\FluentAQL\Exceptions\BindException;
15
use LaravelFreelancerNL\FluentAQL\Expressions\ExpressionInterface as ExpressionInterface;
16
use LaravelFreelancerNL\FluentAQL\QueryBuilder;
17
18
class Builder extends IlluminateQueryBuilder
19
{
20
    /**
21
     * @var Grammar
22
     */
23
    public $grammar;
24
25
    /**
26
     * @var Connection
27
     */
28
    public $connection;
29
30
    /**
31
     * @var QueryBuilder
32
     */
33
    public $aqb;
34
35
    /**
36
     * Alias' are AQL variables
37
     * Sticking with the SQL based naming as this is the Laravel driver.
38
     * @var QueryBuilder
39
     */
40
    protected $aliasRegistry = [];
41
42
    /**
43
     * @override
44
     * Create a new query builder instance.
45
     *
46
     * @param ConnectionInterface $connection
47
     * @param Grammar $grammar
48
     * @param Processor $processor
49
     * @param QueryBuilder|null $aqb
50
     */
51
    public function __construct(ConnectionInterface $connection,
52
                                Grammar $grammar = null,
53
                                Processor $processor = null,
54
                                QueryBuilder $aqb = null)
55
    {
56
        $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...
57
        $this->grammar = $grammar ?: $connection->getQueryGrammar();
58
        $this->processor = $processor ?: $connection->getPostProcessor();
59
        if (! $aqb instanceof QueryBuilder) {
60
            $aqb = new QueryBuilder();
61
        }
62
        $this->aqb = $aqb;
63
    }
64
65
    /**
66
     * Run the query as a "select" statement against the connection.
67
     *
68
     * @return array
69
     */
70
    protected function runSelect()
71
    {
72
        $response = $this->connection->select($this->grammar->compileSelect($this)->aqb);
73
        $this->aqb = new QueryBuilder();
74
75
        return $response;
76
    }
77
78
    /**
79
     * Get the SQL representation of the query.
80
     *
81
     * @return string
82
     */
83
    public function toSql()
84
    {
85
        return $this->grammar->compileSelect($this)->aqb->query;
86
    }
87
88
    /**
89
     * Insert a new record into the database.
90
     * @param array $values
91
     * @return bool
92
     * @throws BindException
93
     */
94
    public function insert(array $values) : bool
95
    {
96
        $response = $this->getConnection()->insert($this->grammar->compileInsert($this, $values)->aqb);
97
        $this->aqb = new QueryBuilder();
98
99
        return $response;
100
    }
101
102
    /**
103
     * Insert a new record and get the value of the primary key.
104
     *
105
     * @param array $values
106
     * @param string|null $sequence
107
     * @return int
108
     * @throws BindException
109
     */
110
    public function insertGetId(array $values, $sequence = null)
111
    {
112
        $response = $this->getConnection()->execute($this->grammar->compileInsertGetId($this, $values, $sequence)->aqb);
0 ignored issues
show
Unused Code introduced by
The call to LaravelFreelancerNL\Aran...r::compileInsertGetId() has too many arguments starting with $sequence. ( Ignorable by Annotation )

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

112
        $response = $this->getConnection()->execute($this->grammar->/** @scrutinizer ignore-call */ compileInsertGetId($this, $values, $sequence)->aqb);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
113
        $this->aqb = new QueryBuilder();
114
115
        return (is_array($response)) ?  end($response) : $response;
0 ignored issues
show
introduced by
The condition is_array($response) is always true.
Loading history...
116
    }
117
118
    /**
119
     * Execute the query as a "select" statement.
120
     *
121
     * @param  array|string  $columns
122
     * @return Collection
123
     */
124
    public function get($columns = ['*'])
125
    {
126
        $results = collect($this->onceWithColumns(Arr::wrap($columns), function () {
127
            return $this->runSelect();
128
        }));
129
130
        return $results;
131
    }
132
133
    /**
134
     * Update a record in the database.
135
     *
136
     * @param  array  $values
137
     * @return int
138
     */
139
    public function update(array $values)
140
    {
141
        $response = $this->connection->update($this->grammar->compileUpdate($this, $values)->aqb);
142
        $this->aqb = new QueryBuilder();
143
144
        return $response;
145
    }
146
147
    /**
148
     * Delete a record from the database.
149
     *
150
     * @param  mixed  $_key
151
     * @return int
152
     */
153
    public function delete($_key = null)
154
    {
155
        $response = $this->connection->delete($this->grammar->compileDelete($this, $_key)->aqb);
156
        $this->aqb = new QueryBuilder();
157
158
        return $response;
159
    }
160
161
    public function registerAlias(string $table, string $alias) : void
162
    {
163
        $this->aliasRegistry[$table] = $alias;
164
    }
165
166
    public function getAlias(string $table) : string
167
    {
168
        return $this->aliasRegistry[$table];
169
    }
170
171
    /**
172
     * Execute an aggregate function on the database.
173
     *
174
     * @param  string  $function
175
     * @param  array   $columns
176
     * @return mixed
177
     */
178
    public function aggregate($function, $columns = ['*'])
179
    {
180
        $results = $this->cloneWithout($this->unions ? [] : ['columns'])
181
            ->setAggregate($function, $columns)
182
            ->get($columns);
183
        if (! $results->isEmpty()) {
184
            return $results[0];
185
        }
186
187
        return false;
188
    }
189
190
191
    /**
192
     * Add a basic where clause to the query.
193
     *
194
     * @param Closure|string|array $column
195
     * @param mixed $operator
196
     * @param mixed $value
197
     * @param string $boolean
198
     * @return Builder
199
     */
200
    public function where($column, $operator = null, $value = null, $boolean = 'and')
201
    {
202
        // If the column is an array, we will assume it is an array of key-value pairs
203
        // and can add them each as a where clause. We will maintain the boolean we
204
        // received when the method was called and pass it into the nested where.
205
        if (is_array($column)) {
206
            return $this->addArrayOfWheres($column, $boolean);
207
        }
208
209
        // Here we will make some assumptions about the operator. If only 2 values are
210
        // passed to the method, we will assume that the operator is an equals sign
211
        // and keep going. Otherwise, we'll require the operator to be passed in.
212
        [$value, $operator] = $this->prepareValueAndOperator(
213
            $value, $operator, func_num_args() === 2
214
        );
215
216
        // If the columns is actually a Closure instance, we will assume the developer
217
        // wants to begin a nested where statement which is wrapped in parenthesis.
218
        // We'll add that Closure to the query then return back out immediately.
219
        if ($column instanceof Closure) {
220
            return $this->whereNested($column, $boolean);
221
        }
222
223
        // If the given operator is not found in the list of valid operators we will
224
        // assume that the developer is just short-cutting the '=' operators and
225
        // we will set the operators to '=' and set the values appropriately.
226
        if ($this->invalidOperator($operator)) {
227
            [$value, $operator] = [$operator, '='];
228
        }
229
230
        // If the value is a Closure, it means the developer is performing an entire
231
        // sub-select within the query and we will need to compile the sub-select
232
        // within the where clause to get the appropriate query record results.
233
        if ($value instanceof Closure) {
234
            return $this->whereSub($column, $operator, $value, $boolean);
235
        }
236
237
        $type = 'Basic';
238
239
        // If the column is making a JSON reference we'll check to see if the value
240
        // is a boolean. If it is, we'll add the raw boolean string as an actual
241
        // value to the query to ensure this is properly handled by the query.
242
        if (Str::contains($column, '->') && is_bool($value)) {
243
            $value = new Expression($value ? 'true' : 'false');
244
245
            if (is_string($column)) {
0 ignored issues
show
introduced by
The condition is_string($column) is always true.
Loading history...
246
                $type = 'JsonBoolean';
247
            }
248
        }
249
250
        // Now that we are working with just a simple query we can put the elements
251
        // in our array and add the query binding to our array of bindings that
252
        // will be bound to each SQL statements when it is finally executed.
253
        $this->wheres[] = compact(
254
            'type', 'column', 'operator', 'value', 'boolean'
255
        );
256
257
        if (! $value instanceof Expression) {
258
            $this->addBinding($value, 'where');
259
        }
260
261
        return $this;
262
    }
263
264
    /**
265
     * Determine if the given operator is supported.
266
     *
267
     * @param  string  $operator
268
     * @return bool
269
     */
270
    protected function invalidOperator($operator)
271
    {
272
273
        return ! in_array(strtolower($operator), $this->operators, true) &&
274
            ! isset($this->grammar->getOperators()[strtolower($operator)]);
275
    }
276
277
278
    /**
279
     * Add an "or where" clause to the query.
280
     *
281
     * @param Closure|string|array  $column
282
     * @param  mixed  $operator
283
     * @param  mixed  $value
284
     * @return IlluminateQueryBuilder|static
285
     */
286
    public function orWhere($column, $operator = null, $value = null)
287
    {
288
        return $this->where($column, $operator, $value, 'or');
289
    }
290
291
    /**
292
     * Add an "order by" clause to the query.
293
     *
294
     * @param  Closure|IlluminateQueryBuilder|string  $column
295
     * @param  string  $direction
296
     * @return $this
297
     *
298
     * @throws InvalidArgumentException
299
     */
300
    public function orderBy($column, $direction = 'asc')
301
    {
302
        if ($this->isQueryable($column)) {
303
            [$query, $bindings] = $this->createSub($column);
304
305
            $column = new Expression('('.$query.')');
306
        }
307
308
        $this->{$this->unions ? 'unionOrders' : 'orders'}[] = [
309
            'column' => $column,
310
            'direction' => $direction,
311
        ];
312
313
        return $this;
314
    }
315
316
    /**
317
     * Add a raw "order by" clause to the query.
318
     *
319
     * @param string|ExpressionInterface $aql
320
     * @param array $bindings
321
     * @return $this
322
     */
323
    public function orderByRaw($aql, $bindings = [])
324
    {
325
        $type = 'Raw';
326
        $column = $aql;
327
        $this->{$this->unions ? 'unionOrders' : 'orders'}[] = compact('type', 'column');
328
329
        return $this;
330
    }
331
332
    /**
333
     * Put the query's results in random order.
334
     *
335
     * @param  string  $seed
336
     * @return $this
337
     */
338
    public function inRandomOrder($seed = '')
339
    {
340
        return $this->orderByRaw($this->grammar->compileRandom($this));
341
    }
342
343
344
}
345