Passed
Push — next ( ee2197...d54041 )
by Bas
02:37
created

QueryBuilder::bindArrayValues()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 5
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 8
ccs 0
cts 6
cp 0
crap 12
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace LaravelFreelancerNL\FluentAQL;
6
7
use LaravelFreelancerNL\FluentAQL\AQL\HasFunctions;
8
use LaravelFreelancerNL\FluentAQL\AQL\HasGraphClauses;
9
use LaravelFreelancerNL\FluentAQL\AQL\HasOperatorExpressions;
10
use LaravelFreelancerNL\FluentAQL\AQL\HasQueryClauses;
11
use LaravelFreelancerNL\FluentAQL\AQL\HasStatementClauses;
12
use LaravelFreelancerNL\FluentAQL\AQL\HasSupportCommands;
13
use LaravelFreelancerNL\FluentAQL\Clauses\Clause;
14
use LaravelFreelancerNL\FluentAQL\Exceptions\BindException;
15
use LaravelFreelancerNL\FluentAQL\Expressions\BindExpression;
16
use LaravelFreelancerNL\FluentAQL\Expressions\Expression;
17
use LaravelFreelancerNL\FluentAQL\Expressions\ExpressionInterface;
18
use LaravelFreelancerNL\FluentAQL\Traits\CompilesPredicates;
19
use LaravelFreelancerNL\FluentAQL\Traits\NormalizesExpressions;
20
21
/**
22
 * Class QueryBuilder
23
 * Fluent ArangoDB AQL Query Builder.
24
 * Creates and compiles AQL queries. Returns all data necessary to run the query,
25
 * including bindings and a list of used read/write collections.
26
 */
27
class QueryBuilder
28
{
29
    use NormalizesExpressions;
30
    use CompilesPredicates;
0 ignored issues
show
Bug introduced by
The trait LaravelFreelancerNL\Flue...aits\CompilesPredicates requires the property $logicalOperator which is not provided by LaravelFreelancerNL\FluentAQL\QueryBuilder.
Loading history...
31
    use HasQueryClauses;
32
    use HasStatementClauses;
33
    use HasGraphClauses;
34
    use HasFunctions;
35
    use HasOperatorExpressions;
36
    use HasSupportCommands;
37
38
    /**
39
     * The database query grammar instance.
40
     *
41
     */
42
    public Grammar $grammar;
43
44
    /**
45
     * The AQL query.
46
     */
47
    public ?string $query = null;
48
49
    /**
50
     * Bindings for $query.
51
     *
52
     * @var array<mixed> $binds
53
     */
54
    public array $binds = [];
55
56
    /**
57
     * List of read/write/exclusive collections required for transactions.
58
     *
59
     * @var array<mixed> $collections
60
     */
61
    public $collections = [];
62
63
    /**
64
     * List of Clauses to be compiled into a query.
65
     *
66
     * @var array<mixed> $commands
67
     */
68
    protected $commands = [];
69
70
    /**
71
     * Registry of variable names used in this query.
72
     *
73
     * @var array<mixed> $variables
74
     */
75
    protected array $variables = [];
76
77
    /**
78
     * ID of the query
79
     * Used as prefix for automatically generated bindings.
80
     *
81
     * @var int
82
     */
83
    protected $queryId = 1;
84
85 22
    public function __construct()
86
    {
87 22
        $this->grammar = new Grammar();
88
89 22
        $this->queryId = spl_object_id($this);
90 22
    }
91
92
    /**
93
     * Add an AQL command (raw AQL and Clauses.
94
     *
95
     * @param Clause|Expression|QueryBuilder $command
96
     *
97
     * @return void
98
     */
99 13
    public function addCommand($command)
100
    {
101 13
        $this->commands[] = $command;
102 13
    }
103
104
    /**
105
     * Get the clause list.
106
     *
107
     * @return mixed
108
     */
109 3
    public function getCommands()
110
    {
111 3
        return $this->commands;
112
    }
113
114
    /**
115
     * Get the last, or a specific, command.
116
     *
117
     * @param int|null $index
118
     *
119
     * @return mixed
120
     */
121 4
    public function getCommand(int $index = null)
122
    {
123 4
        if ($index === null) {
124 1
            return end($this->commands);
125
        }
126
127 3
        return $this->commands[$index];
128
    }
129
130
    /**
131
     * Remove the last, or the specified, Command.
132
     *
133
     * @param int|null $index
134
     * @return bool
135
     */
136 3
    public function removeCommand(int $index = null): bool
137
    {
138 3
        if ($index === null) {
139 1
            return (bool)array_pop($this->commands);
140
        }
141 2
        if (isset($this->commands[$index])) {
142 1
            unset($this->commands[$index]);
143
144 1
            return true;
145
        }
146
147 1
        return false;
148
    }
149
150
    /**
151
     * @param mixed  $collections
152
     * @param string $mode
153
     *
154
     * @return QueryBuilder
155
     */
156 2
    public function registerCollections($collections, $mode = 'write'): self
157
    {
158 2
        if (!is_array($collections)) {
159 2
            $collections = [$collections];
160
        }
161
162 2
        $this->collections[$mode] = array_unique(array_merge($collections));
163
164 2
        return $this;
165
    }
166
167
    /**
168
     * Register variables on declaration for later data normalization.
169
     * @param string|array<mixed>|object $variableName
170
     */
171 8
    public function registerVariable(
172
        string|array|object $variableName
173
    ): self {
174 8
        if ($variableName instanceof ExpressionInterface) {
0 ignored issues
show
introduced by
$variableName is never a sub-type of LaravelFreelancerNL\Flue...ons\ExpressionInterface.
Loading history...
175 1
            $variableName = $variableName->compile($this);
176
        }
177 8
        if (is_string($variableName)) {
0 ignored issues
show
introduced by
The condition is_string($variableName) is always false.
Loading history...
178 8
            $variableName = [$variableName => $variableName];
179
        }
180
181 8
        if (is_array($variableName)) {
0 ignored issues
show
introduced by
The condition is_array($variableName) is always true.
Loading history...
182 8
            $this->variables = array_unique(array_merge($this->variables, $variableName));
183
        }
184
185 8
        return $this;
186
    }
187
188
    /**
189
     * Bind data to a variable.
190
     *
191
     * @param object|array<mixed>|string|int|float|bool|null $data
192
     * @throws BindException
193
     */
194 3
    public function bind(
195
        object|array|string|int|float|bool|null $data,
0 ignored issues
show
Bug introduced by
The type LaravelFreelancerNL\FluentAQL\null was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
196
        string $to = null
197
    ): BindExpression {
198 3
        $this->validateBindVariable($to);
199
200 2
        $to = $this->generateBindVariable($to);
201
202 2
        $this->binds[$to] = $data;
203
204 2
        $to = $this->grammar->formatBind($to, false);
205
206 2
        return new BindExpression($to);
207
    }
208
209
    /**
210
     * @param array<array-key, array<array-key, mixed>|object|scalar|null> $array
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, array<a...ed>|object|scalar|null> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, array<array-key, mixed>|object|scalar|null>.
Loading history...
211
     * @throws BindException
212
     */
213
    protected function bindArrayValues(array $array): void
214
    {
215
        foreach ($array as $key => $value) {
216
            $to = null;
217
            if (is_string($key)) {
218
                $to = $key;
219
            }
220
            $this->bind($value, $to);
221
        }
222
    }
223
224
    /**
225
     * Bind a collection name to a variable.
226
     *
227
     * @throws BindException
228
     */
229 1
    public function bindCollection(
230
        mixed $data,
231
        string $to = null
232
    ): BindExpression {
233 1
        $this->validateBindVariable($to);
234
235 1
        $to = $this->generateBindVariable($to);
236
237 1
        $this->binds[$to] = $data;
238
239 1
        $to = $this->grammar->formatBind($to, true);
240
241 1
        return new BindExpression($to);
242
    }
243
244 4
    protected function validateBindVariable(?string $to): void
245
    {
246 4
        if (isset($to) && !$this->grammar->isBindParameter($to)) {
247 1
            throw new BindException('Invalid bind parameter.');
248
        }
249 3
    }
250
251
    /**
252
     * @param null|string $to
253
     *
254
     * @return string
255
     */
256 3
    protected function generateBindVariable(?string $to): string
257
    {
258 3
        if ($to == null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $to of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
259 3
            $to = $this->queryId . '_' . (count($this->binds) + 1);
260
        }
261 3
        return $to;
262
    }
263
264
265
    /**
266
     * Compile the query with its bindings and collection list.
267
     */
268 8
    public function compile(): self
269
    {
270 8
        $this->query = '';
271
        /** @var Expression|Clause @command */
272 8
        foreach ($this->commands as $command) {
273 7
            $this->query .= ' ' . $command->compile($this);
274
        }
275 7
        $this->query = trim($this->query);
276
277 7
        return $this;
278
    }
279
280
    /**
281
     * @return QueryBuilder $this
282
     */
283 8
    public function get()
284
    {
285 8
        $this->compile();
286
287 7
        return $this;
288
    }
289
290
    /**
291
     * @return int $this
292
     */
293 4
    public function getQueryId(): int
294
    {
295 4
        return $this->queryId;
296
    }
297
298
    /**
299
     * @return string
300
     */
301 6
    public function toAql()
302
    {
303 6
        return $this->get()->query ?: "";
304
    }
305
306 5
    public function __toString()
307
    {
308 5
        return $this->toAql();
309
    }
310
311
    /**
312
     * @return array<mixed>
313
     */
314 7
    public function getVariables(): array
315
    {
316 7
        return $this->variables;
317
    }
318
319
    /**
320
     * @param array<mixed> $arguments
321
     * @return array<mixed>
322
     */
323 1
    public function unsetNullValues(array $arguments): array
324
    {
325 1
        return array_filter(
326 1
            $arguments,
327 1
            function ($value) {
328 1
                return !is_null($value);
329 1
            }
330
        );
331
    }
332
}
333