Test Setup Failed
Pull Request — master (#10)
by Bas
02:56 queued 01:19
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\QueryBuilder;
16
17
class Builder extends IlluminateQueryBuilder
18
{
19
    /**
20
     * @var Grammar
21
     */
22
    public $grammar;
23
24
    /**
25
     * @var Connection
26
     */
27
    public $connection;
28
29
    /**
30
     * @var QueryBuilder
31
     */
32
    public $aqb;
33
34
    /**
35
     * Alias' are AQL variables
36
     * Sticking with the SQL based naming as this is the Laravel driver.
37
     * @var QueryBuilder
38
     */
39
    protected $aliasRegistry = [];
40
41
    /**
42
     * @override
43
     * Create a new query builder instance.
44
     *
45
     * @param ConnectionInterface $connection
46
     * @param Grammar $grammar
47
     * @param Processor $processor
48
     * @param QueryBuilder|null $aqb
49
     */
50
    public function __construct(ConnectionInterface $connection,
51
                                Grammar $grammar = null,
52
                                Processor $processor = null,
53
                                QueryBuilder $aqb = null)
54
    {
55
        $this->connection = $connection;
56
        $this->grammar = $grammar ?: $connection->getQueryGrammar();
57
        $this->processor = $processor ?: $connection->getPostProcessor();
58
        if (! $aqb instanceof QueryBuilder) {
59
            $aqb = new QueryBuilder();
60
        }
61
        $this->aqb = $aqb;
62
    }
63
64
    /**
65
     * Run the query as a "select" statement against the connection.
66
     *
67
     * @return array
68
     */
69
    protected function runSelect()
70
    {
71
        $response = $this->connection->select($this->grammar->compileSelect($this)->aqb);
72
        $this->aqb = new QueryBuilder();
73
74
        return $response;
75
    }
76
77
    /**
78
     * Get the SQL representation of the query.
79
     *
80
     * @return string
81
     */
82
    public function toSql()
83
    {
84
        return $this->grammar->compileSelect($this)->aqb->query;
85
    }
86
87
    /**
88
     * Insert a new record into the database.
89
     * @param array $values
90
     * @return bool
91
     * @throws BindException
92
     */
93
    public function insert(array $values) : bool
94
    {
95
        $response = $this->getConnection()->insert($this->grammar->compileInsert($this, $values)->aqb);
96
        $this->aqb = new QueryBuilder();
97
98
        return $response;
99
    }
100
101
    /**
102
     * Insert a new record and get the value of the primary key.
103
     *
104
     * @param array $values
105
     * @param string|null $sequence
106
     * @return int
107
     * @throws BindException
108
     */
109
    public function insertGetId(array $values, $sequence = null)
110
    {
111
        $response = $this->getConnection()->execute($this->grammar->compileInsertGetId($this, $values, $sequence)->aqb);
112
        $this->aqb = new QueryBuilder();
113
114
        return (is_array($response)) ? end($response) : $response;
115
    }
116
117
    /**
118
     * Execute the query as a "select" statement.
119
     *
120
     * @param  array|string  $columns
121
     * @return Collection
122
     */
123
    public function get($columns = ['*'])
124
    {
125
        $results = collect($this->onceWithColumns(Arr::wrap($columns), function () {
126
            return $this->runSelect();
127
        }));
128
129
        return $results;
130
    }
131
132
    /**
133
     * Update a record in the database.
134
     *
135
     * @param  array  $values
136
     * @return int
137
     */
138
    public function update(array $values)
139
    {
140
        $response = $this->connection->update($this->grammar->compileUpdate($this, $values)->aqb);
141
        $this->aqb = new QueryBuilder();
142
143
        return $response;
144
    }
145
146
    /**
147
     * Delete a record from the database.
148
     *
149
     * @param  mixed  $_key
150
     * @return int
151
     */
152
    public function delete($_key = null)
153
    {
154
        $response = $this->connection->delete($this->grammar->compileDelete($this, $_key)->aqb);
155
        $this->aqb = new QueryBuilder();
156
157
        return $response;
158
    }
159
160
    public function registerAlias(string $table, string $alias) : void
161
    {
162
        $this->aliasRegistry[$table] = $alias;
163
    }
164
165
    public function getAlias(string $table) : string
166
    {
167
        return $this->aliasRegistry[$table];
168
    }
169
170
    /**
171
     * Execute an aggregate function on the database.
172
     *
173
     * @param  string  $function
174
     * @param  array   $columns
175
     * @return mixed
176
     */
177
    public function aggregate($function, $columns = ['*'])
178
    {
179
        $results = $this->cloneWithout($this->unions ? [] : ['columns'])
180
            ->setAggregate($function, $columns)
181
            ->get($columns);
182
        if (! $results->isEmpty()) {
183
            return $results[0];
184
        }
185
186
        return false;
187
    }
188
189
    /**
190
     * Add a basic where clause to the query.
191
     *
192
     * @param Closure|string|array $column
193
     * @param mixed $operator
194
     * @param mixed $value
195
     * @param string $boolean
196
     * @return Builder
197
     */
198
    public function where($column, $operator = null, $value = null, $boolean = 'and')
199
    {
200
        // If the column is an array, we will assume it is an array of key-value pairs
201
        // and can add them each as a where clause. We will maintain the boolean we
202
        // received when the method was called and pass it into the nested where.
203
        if (is_array($column)) {
204
            return $this->addArrayOfWheres($column, $boolean);
205
        }
206
207
        // Here we will make some assumptions about the operator. If only 2 values are
208
        // passed to the method, we will assume that the operator is an equals sign
209
        // and keep going. Otherwise, we'll require the operator to be passed in.
210
        [$value, $operator] = $this->prepareValueAndOperator(
211
            $value, $operator, func_num_args() === 2
212
        );
213
214
        // If the columns is actually a Closure instance, we will assume the developer
215
        // wants to begin a nested where statement which is wrapped in parenthesis.
216
        // We'll add that Closure to the query then return back out immediately.
217
        if ($column instanceof Closure) {
218
            return $this->whereNested($column, $boolean);
219
        }
220
221
        // If the given operator is not found in the list of valid operators we will
222
        // assume that the developer is just short-cutting the '=' operators and
223
        // we will set the operators to '=' and set the values appropriately.
224
        if ($this->invalidOperator($operator)) {
225
            [$value, $operator] = [$operator, '='];
226
        }
227
228
        // If the value is a Closure, it means the developer is performing an entire
229
        // sub-select within the query and we will need to compile the sub-select
230
        // within the where clause to get the appropriate query record results.
231
        if ($value instanceof Closure) {
232
            return $this->whereSub($column, $operator, $value, $boolean);
233
        }
234
235
        $type = 'Basic';
236
237
        // If the column is making a JSON reference we'll check to see if the value
238
        // is a boolean. If it is, we'll add the raw boolean string as an actual
239
        // value to the query to ensure this is properly handled by the query.
240
        if (Str::contains($column, '->') && is_bool($value)) {
241
            $value = new Expression($value ? 'true' : 'false');
242
243
            if (is_string($column)) {
244
                $type = 'JsonBoolean';
245
            }
246
        }
247
248
        // Now that we are working with just a simple query we can put the elements
249
        // in our array and add the query binding to our array of bindings that
250
        // will be bound to each SQL statements when it is finally executed.
251
        $this->wheres[] = compact(
252
            'type', 'column', 'operator', 'value', 'boolean'
253
        );
254
255
        if (! $value instanceof Expression) {
256
            $this->addBinding($value, 'where');
257
        }
258
259
        return $this;
260
    }
261
262
    /**
263
     * Determine if the given operator is supported.
264
     *
265
     * @param  string  $operator
266
     * @return bool
267
     */
268
    protected function invalidOperator($operator)
269
    {
270
        return ! in_array(strtolower($operator), $this->operators, true) &&
271
            ! isset($this->grammar->getOperators()[strtolower($operator)]);
272
    }
273
274
    /**
275
     * Add an "or where" clause to the query.
276
     *
277
     * @param Closure|string|array  $column
278
     * @param  mixed  $operator
279
     * @param  mixed  $value
280
     * @return IlluminateQueryBuilder|static
281
     */
282
    public function orWhere($column, $operator = null, $value = null)
283
    {
284
        return $this->where($column, $operator, $value, 'or');
285
    }
286
287
    /**
288
     * Add an "order by" clause to the query.
289
     *
290
     * @param  Closure|IlluminateQueryBuilder|string  $column
291
     * @param  string  $direction
292
     * @return $this
293
     *
294
     * @throws InvalidArgumentException
295
     */
296
    public function orderBy($column, $direction = 'asc')
297
    {
298
        if ($this->isQueryable($column)) {
299
            [$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...
300
301
            $column = new Expression('('.$query.')');
302
        }
303
304
        $this->{$this->unions ? 'unionOrders' : 'orders'}[] = [
305
            'column' => $column,
306
            'direction' => $direction,
307
        ];
308
309
        return $this;
310
    }
311
312
    /**
313
     * Add a raw "order by" clause to the query.
314
     *
315
     * @param \LaravelFreelancerNL\FluentAQL\Expressions\FunctionExpression $aql
316
     * @param array $bindings
317
     * @return $this
318
     */
319
    public function orderByRaw($aql, $bindings = [])
320
    {
321
        $type = 'Raw';
322
        $column = $aql;
323
        $this->{$this->unions ? 'unionOrders' : 'orders'}[] = compact('type', 'column');
324
325
        return $this;
326
    }
327
328
    /**
329
     * Put the query's results in random order.
330
     *
331
     * @param  string  $seed
332
     * @return $this
333
     */
334
    public function inRandomOrder($seed = '')
335
    {
336
        return $this->orderByRaw($this->grammar->compileRandom($this));
337
    }
338
}
339