Failed Conditions
Branch master (1fae15)
by Bas
08:59
created

src/Query/Builder.php (2 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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;
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);
113
        $this->aqb = new QueryBuilder();
114
115
        return (is_array($response)) ? end($response) : $response;
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)) {
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);
0 ignored issues
show
The variable $query does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
The variable $bindings does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
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