Passed
Branch master (512d52)
by Bas
08:47
created

Builder::toSql()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 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
     * Add a basic where clause to the query.
192
     *
193
     * @param Closure|string|array $column
194
     * @param mixed $operator
195
     * @param mixed $value
196
     * @param string $boolean
197
     * @return Builder
198
     */
199
    public function where($column, $operator = null, $value = null, $boolean = 'and')
200
    {
201
        // If the column is an array, we will assume it is an array of key-value pairs
202
        // and can add them each as a where clause. We will maintain the boolean we
203
        // received when the method was called and pass it into the nested where.
204
        if (is_array($column)) {
205
            return $this->addArrayOfWheres($column, $boolean);
206
        }
207
208
        // Here we will make some assumptions about the operator. If only 2 values are
209
        // passed to the method, we will assume that the operator is an equals sign
210
        // and keep going. Otherwise, we'll require the operator to be passed in.
211
        [$value, $operator] = $this->prepareValueAndOperator(
212
            $value, $operator, func_num_args() === 2
213
        );
214
215
        // If the columns is actually a Closure instance, we will assume the developer
216
        // wants to begin a nested where statement which is wrapped in parenthesis.
217
        // We'll add that Closure to the query then return back out immediately.
218
        if ($column instanceof Closure) {
219
            return $this->whereNested($column, $boolean);
220
        }
221
222
        // If the given operator is not found in the list of valid operators we will
223
        // assume that the developer is just short-cutting the '=' operators and
224
        // we will set the operators to '=' and set the values appropriately.
225
        if ($this->invalidOperator($operator)) {
226
            [$value, $operator] = [$operator, '='];
227
        }
228
229
        // If the value is a Closure, it means the developer is performing an entire
230
        // sub-select within the query and we will need to compile the sub-select
231
        // within the where clause to get the appropriate query record results.
232
        if ($value instanceof Closure) {
233
            return $this->whereSub($column, $operator, $value, $boolean);
234
        }
235
236
        $type = 'Basic';
237
238
        // If the column is making a JSON reference we'll check to see if the value
239
        // is a boolean. If it is, we'll add the raw boolean string as an actual
240
        // value to the query to ensure this is properly handled by the query.
241
        if (Str::contains($column, '->') && is_bool($value)) {
242
            $value = new Expression($value ? 'true' : 'false');
243
244
            if (is_string($column)) {
0 ignored issues
show
introduced by
The condition is_string($column) is always true.
Loading history...
245
                $type = 'JsonBoolean';
246
            }
247
        }
248
249
        // Now that we are working with just a simple query we can put the elements
250
        // in our array and add the query binding to our array of bindings that
251
        // will be bound to each SQL statements when it is finally executed.
252
        $this->wheres[] = compact(
253
            'type', 'column', 'operator', 'value', 'boolean'
254
        );
255
256
        if (! $value instanceof Expression) {
257
            $this->addBinding($value, 'where');
258
        }
259
260
        return $this;
261
    }
262
263
    /**
264
     * Determine if the given operator is supported.
265
     *
266
     * @param  string  $operator
267
     * @return bool
268
     */
269
    protected function invalidOperator($operator)
270
    {
271
        return ! in_array(strtolower($operator), $this->operators, true) &&
272
            ! isset($this->grammar->getOperators()[strtolower($operator)]);
273
    }
274
275
    /**
276
     * Add an "or where" clause to the query.
277
     *
278
     * @param Closure|string|array  $column
279
     * @param  mixed  $operator
280
     * @param  mixed  $value
281
     * @return IlluminateQueryBuilder|static
282
     */
283
    public function orWhere($column, $operator = null, $value = null)
284
    {
285
        return $this->where($column, $operator, $value, 'or');
286
    }
287
288
    /**
289
     * Add an "order by" clause to the query.
290
     *
291
     * @param  Closure|IlluminateQueryBuilder|string  $column
292
     * @param  string  $direction
293
     * @return $this
294
     *
295
     * @throws InvalidArgumentException
296
     */
297
    public function orderBy($column, $direction = 'asc')
298
    {
299
        if ($this->isQueryable($column)) {
300
            [$query, $bindings] = $this->createSub($column);
301
302
            $column = new Expression('('.$query.')');
303
        }
304
305
        $this->{$this->unions ? 'unionOrders' : 'orders'}[] = [
306
            'column' => $column,
307
            'direction' => $direction,
308
        ];
309
310
        return $this;
311
    }
312
313
    /**
314
     * Add a raw "order by" clause to the query.
315
     *
316
     * @param string|ExpressionInterface $aql
317
     * @param array $bindings
318
     * @return $this
319
     */
320
    public function orderByRaw($aql, $bindings = [])
321
    {
322
        $type = 'Raw';
323
        $column = $aql;
324
        $this->{$this->unions ? 'unionOrders' : 'orders'}[] = compact('type', 'column');
325
326
        return $this;
327
    }
328
329
    /**
330
     * Put the query's results in random order.
331
     *
332
     * @param  string  $seed
333
     * @return $this
334
     */
335
    public function inRandomOrder($seed = '')
336
    {
337
        return $this->orderByRaw($this->grammar->compileRandom($this));
338
    }
339
}
340