Passed
Push — feature/laravel-11-support ( 6c6616...4492de )
by Bas
04:19 queued 15s
created

Builder::getConnection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace LaravelFreelancerNL\Aranguent\Query;
4
5
use Exception;
6
use Illuminate\Database\ConnectionInterface as IlluminateConnectionInterface;
7
use Illuminate\Database\Query\Builder as IlluminateQueryBuilder;
8
use Illuminate\Database\Query\Expression;
9
use Illuminate\Database\Query\Grammars\Grammar as IlluminateQueryGrammar;
10
use Illuminate\Database\Query\Processors\Processor as IlluminateProcessor;
11
use LaravelFreelancerNL\Aranguent\Connection;
12
use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsGroups;
13
use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsSearches;
14
use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsInserts;
15
use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsJoins;
16
use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsSelects;
17
use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsSubqueries;
18
use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsUpdates;
19
use LaravelFreelancerNL\Aranguent\Query\Concerns\BuildsWheres;
20
use LaravelFreelancerNL\Aranguent\Query\Concerns\ConvertsIdToKey;
21
use LaravelFreelancerNL\Aranguent\Query\Concerns\HandlesAliases;
22
use LaravelFreelancerNL\Aranguent\Query\Concerns\HandlesBindings;
23
use LaravelFreelancerNL\Aranguent\Query\Enums\VariablePosition;
24
use LaravelFreelancerNL\FluentAQL\QueryBuilder as AQB;
25
use phpDocumentor\Reflection\Types\Boolean;
26
27
class Builder extends IlluminateQueryBuilder
28
{
29
    use BuildsGroups;
0 ignored issues
show
introduced by
The trait LaravelFreelancerNL\Aran...y\Concerns\BuildsGroups requires some properties which are not provided by LaravelFreelancerNL\Aranguent\Query\Builder: $end, $start
Loading history...
30
    use BuildsInserts;
31
    use BuildsJoins;
32
    use BuildsSearches;
33
    use BuildsSelects;
34
    use BuildsSubqueries;
35
    use BuildsUpdates;
36
    use BuildsWheres;
37
    use ConvertsIdToKey;
38
    use HandlesAliases;
39
    use HandlesBindings;
40
    public AQB $aqb;
41
42
    /**
43
     * The current query value bindings.
44
     *
45
     * @var array<mixed>
46
     */
47
    public $bindings = [
48
        'preIterationVariables' => [],
49
        'from' => [],
50
        'fromOptions' => [],
51
        'search' => [],
52
        'join' => [],
53
        'postIterationVariables' => [],
54
        'where' => [],
55
        'groupBy' => [],
56
        'having' => [],
57
        'order' => [],
58
        'union' => [],
59
        'unionOrder' => [],
60
        'select' => [],
61
        'insert' => [],
62
        'update' => [],
63
        'upsert' => [],
64
    ];
65
66
67
    /**
68
     * @var Connection
69
     */
70
    public $connection;
71
72
    /**
73
     * @var IlluminateQueryGrammar
74
     */
75
    public $grammar;
76
77
    /**
78
     * 'from' options.
79
     *
80
     * @var array<mixed>
81
     */
82
    public $fromOptions = [];
83
84
    /**
85
     * The current query value bindings.
86
     *
87
     * @var null|array<mixed>
88
     */
89
    public ?array $search = null;
90
91
    /**
92
     * The query variables that should be set before traversals (for/joins).
93
     *
94
     * @var array<mixed>
95
     */
96
    public $preIterationVariables = [];
97
98
    /**
99
     * The query variables that should be set after traversals (for/joins).
100
     *
101
     * @var array<mixed>
102
     */
103
    public $postIterationVariables = [];
104
105
    /**
106
     * ID of the query
107
     * Used as prefix for automatically generated bindings.
108
     *
109
     * @var int
110
     */
111
    protected $queryId = 1;
112
113
    /**
114
     * @override
115
     * Create a new query builder instance.
116
     */
117 271
    public function __construct(
118
        IlluminateConnectionInterface $connection,
119
        IlluminateQueryGrammar        $grammar = null,
120
        IlluminateProcessor           $processor = null,
121
        AQB                           $aqb = null
122
    ) {
123
        assert($connection instanceof IlluminateConnectionInterface);
124
        assert($processor instanceof IlluminateProcessor);
125
126 271
        parent::__construct($connection, $grammar, $processor);
127
128 271
        if (!$aqb instanceof AQB) {
129 271
            $aqb = new AQB();
130
        }
131 271
        $this->aqb = $aqb;
132
133 271
        $this->queryId = spl_object_id($this);
134
    }
135
136 32
    public function getQueryId(): int
137
    {
138 32
        return $this->queryId;
139
    }
140
141
    /**
142
     * Get the current query value bindings in a flattened array.
143
     *
144
     * @return array<mixed>
145
     */
146 238
    public function getBindings()
147
    {
148 238
        $extractedBindings = [];
149 238
        foreach ($this->bindings as $typeBinds) {
150 238
            foreach ($typeBinds as $key => $value) {
151 155
                $extractedBindings[$key] = $value;
152
            }
153
        }
154
155 238
        return $extractedBindings;
156
    }
157
158
    /**
159
     * Run a pagination count query.
160
     *
161
     * @param array<mixed> $columns
162
     * @return array<mixed>
163
     */
164 3
    protected function runPaginationCountQuery($columns = ['*'])
165
    {
166 3
        $without = $this->unions ? ['orders', 'limit', 'offset'] : ['columns', 'orders', 'limit', 'offset'];
167
168 3
        $closeResults = $this->cloneWithout($without)
169 3
            ->cloneWithoutBindings($this->unions ? ['order'] : ['select', 'order'])
170 3
            ->setAggregate('count', $this->withoutSelectAliases($columns))
171 3
            ->get()->all();
172
173 3
        return $closeResults;
174
    }
175
176
    /**
177
     * Delete records from the database.
178
     *
179
     * @param mixed $id
180
     * @return int
181
     */
182 28
    public function delete($id = null)
183
    {
184 28
        $table = (string) $this->grammar->getValue($this->from);
185
186
        // If an ID is passed to the method, we will set the where clause to check the
187
        // ID to let developers to simply and quickly remove a single row from this
188
        // database without manually specifying the "where" clauses on the query.
189 28
        if (!is_null($id)) {
190 1
            $this->where($table . '._key', '=', $id);
191
        }
192
193 28
        $this->applyBeforeQueryCallbacks();
194
195 28
        return $this->connection->delete(
196 28
            $this->grammar->compileDelete($this),
197 28
            $this->cleanBindings(
198 28
                $this->grammar->prepareBindingsForDelete($this->bindings)
199 28
            )
200 28
        );
201
    }
202
203
    /**
204
     * Determine if any rows exist for the current query.
205
     *
206
     * @return bool
207
     */
208 6
    public function exists()
209
    {
210 6
        $this->applyBeforeQueryCallbacks();
211
212 6
        $results = $this->connection->select(
213 6
            $this->grammar->compileExists($this),
214 6
            $this->getBindings(),
215 6
            !$this->useWritePdo
216 6
        );
217
218
        // If the results have rows, we will get the row and see if the exists column is a
219
        // boolean true. If there are no results for this query we will return false as
220
        // there are no rows for this query at all, and we can return that info here.
221 6
        if (isset($results[0])) {
222 6
            $results = (array) $results[0];
223
224 6
            return (bool) $results['exists'];
225
        }
226
227
        return false;
228
    }
229
230
231
    /**
232
     * Execute an aggregate function on the database.
233
     *
234
     * @param string $function
235
     * @param array<mixed> $columns
236
     * @return mixed
237
     */
238 37
    public function aggregate($function, $columns = ['*'])
239
    {
240 37
        $results = $this->cloneWithout($this->unions ? [] : ['columns'])
241 37
            ->setAggregate($function, $columns)
242 37
            ->get($columns);
243
244 37
        if (!$results->isEmpty()) {
245 37
            return ($results->first())->aggregate;
246
        }
247
248
        return false;
249
    }
250
251
    /**
252
     * Set a variable
253
     * @param string $variable
254
     * @param IlluminateQueryBuilder|Expression|array<mixed>|Int|Float|String|Boolean $value
255
     * @param string|VariablePosition $variablePosition
256
     * @return Builder
257
     */
258 7
    public function set(
259
        string $variable,
260
        IlluminateQueryBuilder|Expression|array|Boolean|Int|Float|String $value,
261
        VariablePosition|string $variablePosition = VariablePosition::preIterations
262
    ): Builder {
263 7
        if (is_string($variablePosition)) {
264 3
            $variablePosition = VariablePosition::tryFrom($variablePosition) ?? VariablePosition::preIterations;
265
        }
266 7
        if ($value instanceof Expression) {
0 ignored issues
show
introduced by
$value is never a sub-type of Illuminate\Database\Query\Expression.
Loading history...
267 5
            $this->{$variablePosition->value}[$variable] = $value->getValue($this->grammar);
268
269 5
            return $this;
270
        }
271
272 3
        if ($value instanceof Builder) {
0 ignored issues
show
introduced by
$value is never a sub-type of LaravelFreelancerNL\Aranguent\Query\Builder.
Loading history...
273
274 1
            [$subquery] = $this->createSub($value);
275
276 1
            $this->{$variablePosition->value}[$variable] = $subquery;
277
278 1
            return $this;
279
        }
280 2
        $this->{$variablePosition->value}[$variable] = $this->bindValue($value, $variablePosition->value);
281
282 2
        return $this;
283
    }
284
285 221
    public function isVariable(string $value): bool
286
    {
287
        if (
288 221
            key_exists($value, $this->preIterationVariables)
289 221
            ||  key_exists($value, $this->postIterationVariables)
290
        ) {
291 1
            return true;
292
        }
293
294 221
        return false;
295
    }
296
297
    /**
298
     * @param mixed $value
299
     * @param array<mixed> $variables
300
     * @return bool
301
     */
302 241
    public function isReference(mixed $value, array $variables = []): bool
303
    {
304 241
        if (!is_string($value) || empty($value)) {
305 90
            return false;
306
        }
307
308 238
        if (empty($variables)) {
309 238
            $variables = array_merge(
310 238
                array_keys($this->preIterationVariables),
311 238
                array_keys($this->postIterationVariables),
312 238
                $this->tableAliases,
313 238
            );
314
        }
315
316 238
        $variablesRegex = implode('|', $variables);
317
318 238
        return (bool) preg_match(
319 238
            '/^\`?('
320 238
            . $variablesRegex
321 238
            . '|CURRENT|NEW|OLD)\`?(\[\`.+\`\]|\[[\d\w\*]*\])*(\.(\`.+\`|@?[\d\w]*)(\[\`.+\`\]|\[[\d\w\*]*\])*)*$/',
322 238
            $value
323 238
        );
324
    }
325
326
327
    /**
328
     * Get the database connection instance.
329
     *
330
     * @return Connection
331
     */
332 114
    public function getConnection()
333
    {
334 114
        return $this->connection;
335
    }
336
337
    /**
338
     * Prepend the database name if the given query is on another database.
339
     *
340
     * @param mixed $query
341
     * @return mixed
342
     * @throws Exception
343
     */
344
    protected function prependDatabaseNameIfCrossDatabaseQuery($query)
345
    {
346
        if ($query->getConnection()->getDatabaseName() !==
347
            $this->getConnection()->getDatabaseName()) {
348
            $databaseName = $query->getConnection()->getDatabaseName();
349
350
            if (!str_starts_with($query->from, $databaseName) && !str_contains($query->from, '.')) {
351
                throw new Exception(message: 'ArangoDB does not support cross database queries.');
352
            }
353
        }
354
355
        return $query;
356
    }
357
358
    /**
359
     * Get the AQL representation of the query.
360
     */
361 3
    public function toAql(): string
362
    {
363 3
        return $this->toSql();
364
    }
365
}
366