Failed Conditions
Push — refactor/improve-static-analys... ( ba8000...c6edde )
by Bas
14:06
created

Builder::getConnection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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