Passed
Branch next (97e380)
by Bas
03:07
created

Connection::query()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 6
ccs 5
cts 5
cp 1
crap 1
rs 10
c 1
b 0
f 0
1
<?php
2
3
namespace LaravelFreelancerNL\Aranguent;
4
5
use ArangoClient\ArangoClient;
6
use Illuminate\Database\Connection as IlluminateConnection;
7
use Iterator;
8
use LaravelFreelancerNL\Aranguent\Concerns\DetectsDeadlocks;
9
use LaravelFreelancerNL\Aranguent\Concerns\DetectsLostConnections;
10
use LaravelFreelancerNL\Aranguent\Concerns\HandlesArangoDb;
11
use LaravelFreelancerNL\Aranguent\Concerns\ManagesTransactions;
12
use LaravelFreelancerNL\Aranguent\Query\Builder as QueryBuilder;
13
use LaravelFreelancerNL\Aranguent\Query\Grammar as QueryGrammar;
14
use LaravelFreelancerNL\Aranguent\Query\Processor;
15
use LaravelFreelancerNL\Aranguent\Schema\Builder as SchemaBuilder;
16
use LaravelFreelancerNL\FluentAQL\QueryBuilder as FluentAQL;
17
18
class Connection extends IlluminateConnection
19
{
20
    use HandlesArangoDb;
21
    use DetectsDeadlocks;
22
    use DetectsLostConnections;
23
    use ManagesTransactions;
24
25
    protected ArangoClient $arangoClient;
26
27
    protected $database;
28
29
    /**
30
     * The ArangoDB driver name.
31
     *
32
     * @var string
33
     */
34
    protected string $driverName = 'arangodb';
35
36
    /**
37
     * Connection constructor.
38
     *
39
     * @param array $config
40
     */
41 134
    public function __construct($config = [])
42
    {
43 134
        $this->config = $config;
44
45 134
        $this->database = (isset($this->config['database'])) ? $this->config['database'] : null;
46 134
        $this->tablePrefix = isset($this->config['tablePrefix']) ? $this->config['tablePrefix'] : null;
47
48
        // activate and set the database client connection
49 134
        $this->arangoClient = new ArangoClient($this->arangoClientConfig($this->config));
50
51
        // We need to initialize a query grammar and the query post processors
52
        // which are both very important parts of the database abstractions
53
        // so we initialize these to their default values while starting.
54 134
        $this->useDefaultQueryGrammar();
55
56 134
        $this->useDefaultPostProcessor();
57 134
    }
58
59 134
    protected function arangoClientConfig(array $config): array
60
    {
61 134
        unset($config['name']);
62 134
        unset($config['driver']);
63
64 134
        return $config;
65
    }
66
67
    /**
68
     * Get a schema builder instance for the connection.
69
     *
70
     * @return SchemaBuilder
71
     */
72 134
    public function getSchemaBuilder(): SchemaBuilder
73
    {
74 134
        if (is_null($this->schemaGrammar)) {
75
            $this->useDefaultSchemaGrammar();
76
        }
77
78 134
        return new SchemaBuilder($this);
79
    }
80
81
    /**
82
     * Get the default query grammar instance.
83
     *
84
     * @return QueryGrammar
85
     */
86 134
    protected function getDefaultQueryGrammar(): QueryGrammar
87
    {
88 134
        return new QueryGrammar();
89
    }
90
91
    /**
92
     * Get the default post processor instance.
93
     *
94
     * @return Processor
95
     */
96 134
    protected function getDefaultPostProcessor(): Processor
97
    {
98 134
        return new Processor();
99
    }
100
101
    /**
102
     * Run a select statement against the database and returns a generator.
103
     * ($useReadPdo is a dummy to adhere to the interface).
104
     *
105
     * @param  string  $query
106
     * @param  array  $bindings
107
     * @param  bool|null  $useReadPdo
108
     * @return Iterator|null
109
     */
110
    public function cursor($query, $bindings = [], $useReadPdo = null): ?Iterator
111
    {
112
        // Usage of a separate DB to read date isn't supported at this time
113
        $useReadPdo = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $useReadPdo is dead and can be removed.
Loading history...
114
115
        return $this->run($query, $bindings, function ($query, $bindings) {
116
            if ($this->pretending()) {
117
                return [];
118
            }
119
120
            $statement = $this->arangoClient->prepare($query, $bindings);
121
122
            return $statement->execute();
123
        });
124
    }
125
126
    /**
127
     * Execute an AQL statement and return the boolean result.
128
     *
129
     * @param string|FluentAQL $query
130
     * @param array            $bindings
131
     *
132
     * @return bool
133
     */
134 134
    public function statement($query, $bindings = []): bool
135
    {
136 134
        [$query, $bindings] = $this->handleQueryBuilder(
137 134
            $query,
138
            $bindings
139
        );
140
141 134
        return $this->run($query, $bindings, function ($query, $bindings) {
142 134
            if ($this->pretending()) {
143
                return true;
144
            }
145
146 134
            $statement = $this->arangoClient->prepare($query, $bindings);
147
148 134
            $statement->execute();
149
150 134
            $affectedDocumentCount = $statement->getWritesExecuted();
151 134
            $this->recordsHaveBeenModified($changed = $affectedDocumentCount > 0);
152
153 134
            return $changed;
154 134
        });
155
    }
156
157
    /**
158
     * Run an AQL statement and get the number of rows affected.
159
     *
160
     * @param string|FluentAQL $query
161
     * @param array            $bindings
162
     *
163
     * @return int
164
     */
165 19
    public function affectingStatement($query, $bindings = []): int
166
    {
167 19
        [$query, $bindings] = $this->handleQueryBuilder(
168 19
            $query,
169
            $bindings
170
        );
171
172 19
        return $this->run($query, $bindings, function () use ($query, $bindings) {
173 19
            if ($this->pretending()) {
174
                return 0;
175
            }
176
177
            // For update or delete statements, we want to get the number of rows affected
178
            // by the statement and return that back to the developer. We'll first need
179
            // to execute the statement and get the executed writes from the extra.
180 19
            $statement = $this->arangoClient->prepare($query, $bindings);
181
182 19
            $statement->execute();
183
184 19
            $affectedDocumentCount = $statement->getWritesExecuted();
185
186 19
            $this->recordsHaveBeenModified($affectedDocumentCount > 0);
187
188 19
            return $affectedDocumentCount;
189 19
        });
190
    }
191
192
    /**
193
     * Run a raw, unprepared query against the connection.
194
     *
195
     * @param  string  $query
196
     *
197
     * @return bool
198
     */
199
    public function unprepared($query): bool
200
    {
201
        return $this->run($query, [], function ($query) {
202
            if ($this->pretending()) {
203
                return true;
204
            }
205
206
            $statement = $$this->arangoClient->prepare($query);
207
            $statement->execute();
208
            $affectedDocumentCount = $statement->getWritesExecuted();
209
            $change = $affectedDocumentCount > 0;
210
211
            $this->recordsHaveBeenModified($change);
212
213
            return $change;
214
        });
215
    }
216
217
    /**
218
     * Returns the query execution plan. The query will not be executed.
219
     *
220
     * @param  string  $query
221
     * @param  array  $bindings
222
     *
223
     * @return array
224
     */
225 1
    public function explain(string $query, $bindings = []): array
226
    {
227 1
        $statement = $this->arangoClient->prepare($query, $bindings);
228
229 1
        return $statement->explain();
230
    }
231
232
    /**
233
     * Run a select statement against the database.
234
     *
235
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
236
     *
237
     * @param string|FluentAQL $query
238
     * @param array            $bindings
239
     * @param bool             $useReadPdo
240
     * @return mixed
241
     */
242 134
    public function select($query, $bindings = [], $useReadPdo = true)
243
    {
244 134
        return $this->execute($query, $bindings, $useReadPdo);
245
    }
246
247
    /**
248
     * Run an AQL query against the database and return the results.
249
     *
250
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
251
     *
252
     * @param string|FluentAQL $query
253
     * @param array<mixed>|null     $bindings
254
     * @param bool             $useReadPdo
255
     *
256
     * @return mixed
257
     */
258 134
    public function execute($query, ?array $bindings = [], $useReadPdo = true)
0 ignored issues
show
Unused Code introduced by
The parameter $useReadPdo is not used and could be removed. ( Ignorable by Annotation )

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

258
    public function execute($query, ?array $bindings = [], /** @scrutinizer ignore-unused */ $useReadPdo = true)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
259
    {
260
        // Usage of a separate DB to read date isn't supported at this time
261 134
        $useReadPdo = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $useReadPdo is dead and can be removed.
Loading history...
262
263 134
        if ($query instanceof FluentAQL) {
264 62
            $bindings = $query->binds;
265 62
            $query = $query->query;
266
        }
267
268 134
        return $this->run($query, $bindings, function () use ($query, $bindings) {
269 134
            if ($this->pretending()) {
270
                return [];
271
            }
272
273 134
            $statement = $this->arangoClient->prepare($query, $bindings);
274 134
            $statement->execute();
275
276 134
            return $statement->fetchAll();
277 134
        });
278
    }
279
280
    /**
281
     * Get a new query builder instance.
282
     *
283
     * @return QueryBuilder
284
     */
285 73
    public function query(): QueryBuilder
286
    {
287 73
        return new QueryBuilder(
288 73
            $this,
289 73
            $this->getQueryGrammar(),
0 ignored issues
show
Bug introduced by
$this->getQueryGrammar() of type Illuminate\Database\Query\Grammars\Grammar is incompatible with the type LaravelFreelancerNL\Aranguent\Query\Grammar|null expected by parameter $grammar of LaravelFreelancerNL\Aran...\Builder::__construct(). ( Ignorable by Annotation )

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

289
            /** @scrutinizer ignore-type */ $this->getQueryGrammar(),
Loading history...
290 73
            $this->getPostProcessor()
291
        );
292
    }
293
294
    /**
295
     * Get the collection prefix for the connection.
296
     *
297
     * @return string
298
     */
299
    public function getTablePrefix(): string
300
    {
301
        return $this->tablePrefix;
302
    }
303
304
    /**
305
     * Disconnect from the underlying ArangoDB connection.
306
     *
307
     * @return void
308
     */
309
    public function disconnect()
310
    {
311
        $this->transactions = 0;
312
313
        $this->arangoClient = null;
314
    }
315
316
    /**
317
     * Reconnect to the database if a Arango connection is missing.
318
     *
319
     * @return void
320
     */
321 134
    protected function reconnectIfMissingConnection()
322
    {
323 134
        if (is_null($this->arangoClient)) {
324
            $this->reconnect();
325
        }
326 134
    }
327
328 134
    public function getArangoClient(): ArangoClient
329
    {
330 134
        return $this->arangoClient;
331
    }
332
333
    /**
334
     * @param  string  $database
335
     */
336 134
    public function setDatabaseName($database): void
337
    {
338 134
        $this->database = $database;
339 134
        $this->arangoClient->setDatabase($database);
340 134
    }
341
342 2
    public function getDatabaseName(): string
343
    {
344 2
        return $this->database;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->database could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
345
    }
346
347
    /**
348
     * @param  FluentAQL|string  $query
349
     * @param  array  $bindings
350
     * @return array
351
     */
352 134
    private function handleQueryBuilder($query, array $bindings): array
353
    {
354 134
        if ($query instanceof FluentAQL) {
355 71
            $bindings = $query->binds;
356 71
            $query = $query->query;
357
        }
358 134
        return [$query, $bindings];
359
    }
360
}
361