Builder::prependDatabaseNameIfCrossDatabaseQuery()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 6
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 12
ccs 0
cts 7
cp 0
crap 20
rs 10
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 395
    public function __construct(
118
        IlluminateConnectionInterface  $connection,
119
        ?IlluminateQueryGrammar        $grammar = null,
120
        ?IlluminateProcessor           $processor = null,
121
        ?AQB                           $aqb = null,
122
    ) {
123
        assert($processor instanceof IlluminateProcessor);
124
125 395
        parent::__construct($connection, $grammar, $processor);
126
127 395
        if (!$aqb instanceof AQB) {
128 395
            $aqb = new AQB();
129
        }
130 395
        $this->aqb = $aqb;
131
132 395
        $this->queryId = spl_object_id($this);
133
    }
134
135 39
    public function getQueryId(): int
136
    {
137 39
        return $this->queryId;
138
    }
139
140
    /**
141
     * Get the current query value bindings in a flattened array.
142
     *
143
     * @return array<mixed>
144
     */
145 356
    public function getBindings()
146
    {
147 356
        $extractedBindings = [];
148 356
        foreach ($this->bindings as $typeBinds) {
149 356
            foreach ($typeBinds as $key => $value) {
150 270
                $extractedBindings[$key] = $value;
151
            }
152
        }
153
154 356
        return $extractedBindings;
155
    }
156
157
    /**
158
     * Run a pagination count query.
159
     *
160
     * @param array<mixed> $columns
161
     * @return array<mixed>
162
     */
163 3
    protected function runPaginationCountQuery($columns = ['*'])
164
    {
165 3
        $without = $this->unions ? ['orders', 'limit', 'offset'] : ['columns', 'orders', 'limit', 'offset'];
166
167 3
        $closeResults = $this->cloneWithout($without)
168 3
            ->cloneWithoutBindings($this->unions ? ['order'] : ['select', 'order'])
169 3
            ->setAggregate('count', $this->withoutSelectAliases($columns))
0 ignored issues
show
Bug introduced by
$this->withoutSelectAliases($columns) of type array<mixed,Illuminate\C...uery\Expression|string> is incompatible with the type array<mixed,Illuminate\C...uery\Expression|string> expected by parameter $columns of Illuminate\Database\Query\Builder::setAggregate(). ( Ignorable by Annotation )

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

169
            ->setAggregate('count', /** @scrutinizer ignore-type */ $this->withoutSelectAliases($columns))
Loading history...
170 3
            ->get()->all();
171
172 3
        return $closeResults;
173
    }
174
175
    /**
176
     * Delete records from the database.
177
     *
178
     * @param mixed $id
179
     * @return int
180
     */
181 35
    public function delete($id = null)
182
    {
183 35
        $table = (string) $this->grammar->getValue($this->from);
184
185
        // If an ID is passed to the method, we will set the where clause to check the
186
        // ID to let developers to simply and quickly remove a single row from this
187
        // database without manually specifying the "where" clauses on the query.
188 35
        if (!is_null($id)) {
189 1
            $this->where($table . '._key', '=', $id);
190
        }
191
192 35
        $this->applyBeforeQueryCallbacks();
193
194 35
        return $this->connection->delete(
195 35
            $this->grammar->compileDelete($this),
196 35
            $this->cleanBindings(
197 35
                $this->grammar->prepareBindingsForDelete($this->bindings),
198 35
            ),
199 35
        );
200
    }
201
202
    /**
203
     * Determine if any rows exist for the current query.
204
     *
205
     * @return bool
206
     */
207 18
    public function exists()
208
    {
209 18
        $this->applyBeforeQueryCallbacks();
210
211 18
        $results = $this->connection->select(
212 18
            $this->grammar->compileExists($this),
213 18
            $this->getBindings(),
214 18
            !$this->useWritePdo,
215 18
        );
216
217
        // If the results have rows, we will get the row and see if the exists column is a
218
        // boolean true. If there are no results for this query we will return false as
219
        // there are no rows for this query at all, and we can return that info here.
220 18
        if (isset($results[0])) {
221 18
            $results = (array) $results[0];
222
223 18
            return (bool) $results['exists'];
224
        }
225
226
        return false;
227
    }
228
229
230
    /**
231
     * Execute an aggregate function on the database.
232
     *
233
     * @param string $function
234
     * @param array<mixed> $columns
235
     * @return mixed
236
     */
237 79
    public function aggregate($function, $columns = ['*'])
238
    {
239 79
        $results = $this->cloneWithout($this->unions ? [] : ['columns'])
240 79
            ->setAggregate($function, $columns)
241 79
            ->get($columns);
242
243 79
        if (!$results->isEmpty()) {
244 79
            return ($results->first())->aggregate;
245
        }
246
247
        return false;
248
    }
249
250
    /**
251
     * Set a variable
252
     * @param string $variable
253
     * @param IlluminateQueryBuilder|Expression|array<mixed>|Int|Float|String|Boolean $value
254
     * @param string|VariablePosition $variablePosition
255
     * @return Builder
256
     */
257 7
    public function set(
258
        string $variable,
259
        IlluminateQueryBuilder|Expression|array|Boolean|Int|Float|String $value,
260
        VariablePosition|string $variablePosition = VariablePosition::preIterations,
261
    ): Builder {
262 7
        if (is_string($variablePosition)) {
263 3
            $variablePosition = VariablePosition::tryFrom($variablePosition) ?? VariablePosition::preIterations;
264
        }
265 7
        if ($value instanceof Expression) {
0 ignored issues
show
introduced by
$value is never a sub-type of Illuminate\Database\Query\Expression.
Loading history...
266 5
            $this->{$variablePosition->value}[$variable] = $value->getValue($this->grammar);
267
268 5
            return $this;
269
        }
270
271 3
        if ($value instanceof Builder) {
0 ignored issues
show
introduced by
$value is never a sub-type of LaravelFreelancerNL\Aranguent\Query\Builder.
Loading history...
272
273 1
            [$subquery] = $this->createSub($value);
274
275 1
            $this->{$variablePosition->value}[$variable] = $subquery;
276
277 1
            return $this;
278
        }
279 2
        $this->{$variablePosition->value}[$variable] = $this->bindValue($value, $variablePosition->value);
280
281 2
        return $this;
282
    }
283
284 336
    public function isVariable(string $value): bool
285
    {
286
        if (
287 336
            key_exists($value, $this->preIterationVariables)
288 336
            ||  key_exists($value, $this->postIterationVariables)
289
        ) {
290 1
            return true;
291
        }
292
293 336
        return false;
294
    }
295
296
    /**
297
     * @param mixed $value
298
     * @param array<mixed> $variables
299
     * @return bool
300
     */
301 364
    public function isReference(mixed $value, array $variables = []): bool
302
    {
303 364
        if (!is_string($value) || empty($value)) {
304 177
            return false;
305
        }
306
307 356
        if (empty($variables)) {
308 356
            $variables = array_merge(
309 356
                array_keys($this->preIterationVariables),
310 356
                array_keys($this->postIterationVariables),
311 356
                $this->tableAliases,
312 356
            );
313
        }
314
315 356
        $variablesRegex = implode('|', $variables);
316
317 356
        return (bool) preg_match(
318 356
            '/^\`?('
319 356
            . $variablesRegex
320 356
            . '|CURRENT|NEW|OLD)\`?(\[\`.+\`\]|\[[\d\w\*]*\])*(\.(\`.+\`|@?[\d\w]*)(\[\`.+\`\]|\[[\d\w\*]*\])*)*$/',
321 356
            $value,
322 356
        );
323
    }
324
325
326
    /**
327
     * Get the database connection instance.
328
     *
329
     * @return Connection
330
     */
331 207
    public function getConnection()
332
    {
333 207
        return $this->connection;
334
    }
335
336
    /**
337
     * Prepend the database name if the given query is on another database.
338
     *
339
     * @param mixed $query
340
     * @return mixed
341
     * @throws Exception
342
     */
343
    protected function prependDatabaseNameIfCrossDatabaseQuery($query)
344
    {
345
        if ($query->getConnection()->getDatabaseName() !==
346
            $this->getConnection()->getDatabaseName()) {
347
            $databaseName = $query->getConnection()->getDatabaseName();
348
349
            if (!str_starts_with($query->from, $databaseName) && !str_contains($query->from, '.')) {
350
                throw new Exception(message: 'ArangoDB does not support cross database queries.');
351
            }
352
        }
353
354
        return $query;
355
    }
356
357
    /**
358
     * Get the AQL representation of the query.
359
     */
360 3
    public function toAql(): string
361
    {
362 3
        return $this->toSql();
363
    }
364
}
365