Passed
Push — next ( b7ca98...230762 )
by Bas
12:22
created

Connection::reconnect()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
c 1
b 0
f 0
dl 0
loc 9
ccs 4
cts 5
cp 0.8
rs 10
cc 2
nc 2
nop 0
crap 2.032
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 ArangoQueryBuilder;
17
use LaravelFreelancerNL\FluentAQL\QueryBuilder as FluentAQL;
18
use LogicException;
19
use Spatie\DataTransferObject\Exceptions\UnknownProperties;
20
use stdClass;
21
22
class Connection extends IlluminateConnection
23
{
24
    use HandlesArangoDb;
25
    use DetectsDeadlocks;
26
    use DetectsLostConnections;
27
    use ManagesTransactions;
28
29
    protected ?ArangoClient $arangoClient = null;
30
31
    protected $database;
32
33
    /**
34
     * The ArangoDB driver name.
35
     *
36
     * @var string
37
     */
38
    protected string $driverName = 'arangodb';
39
40
    /**
41
     * Connection constructor.
42
     *
43
     * @param array $config
44
     * @throws UnknownProperties
45
     */
46 172
    public function __construct($config = [])
47
    {
48 172
        $this->config = $config;
49
50 172
        $this->database = (isset($this->config['database'])) ? $this->config['database'] : null;
51 172
        $this->tablePrefix = $this->config['tablePrefix'] ?? null;
52
53
        // activate and set the database client connection
54 172
        $this->arangoClient = new ArangoClient($this->config);
55
56
        // We need to initialize a query grammar and the query post processors
57
        // which are both very important parts of the database abstractions
58
        // so we initialize these to their default values while starting.
59 172
        $this->useDefaultQueryGrammar();
60
61 172
        $this->useDefaultPostProcessor();
62 172
    }
63
64
    /**
65
     * Get a schema builder instance for the connection.
66
     *
67
     * @return SchemaBuilder
68
     */
69 126
    public function getSchemaBuilder(): SchemaBuilder
70
    {
71 126
        if (is_null($this->schemaGrammar)) {
72
            $this->useDefaultSchemaGrammar();
73
        }
74
75 126
        return new SchemaBuilder($this);
76
    }
77
78
    /**
79
     * Get the default query grammar instance.
80
     *
81
     * @return QueryGrammar
82
     */
83 172
    protected function getDefaultQueryGrammar(): QueryGrammar
84
    {
85 172
        return new QueryGrammar();
86
    }
87
88
    /**
89
     * Get the default post processor instance.
90
     *
91
     * @return Processor
92
     */
93 172
    protected function getDefaultPostProcessor(): Processor
94
    {
95 172
        return new Processor();
96
    }
97
98
    /**
99
     * Run a select statement against the database and returns a generator.
100
     * ($useReadPdo is a dummy to adhere to the interface).
101
     *
102
     * @param  string  $query
103
     * @param  array  $bindings
104
     * @param  bool|null  $useReadPdo
105
     * @return Iterator|null
106
     */
107
    public function cursor($query, $bindings = [], $useReadPdo = null): ?Iterator
108
    {
109
        // Usage of a separate DB to read date isn't supported at this time
110
        $useReadPdo = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $useReadPdo is dead and can be removed.
Loading history...
111
112
        return $this->run($query, $bindings, function ($query, $bindings) {
113
            if ($this->pretending()) {
114
                return [];
115
            }
116
117
            $statement = $this->arangoClient->prepare($query, $bindings);
0 ignored issues
show
Bug introduced by
The method prepare() does not exist on null. ( Ignorable by Annotation )

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

117
            /** @scrutinizer ignore-call */ 
118
            $statement = $this->arangoClient->prepare($query, $bindings);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
118
119
            return $statement->execute();
120
        });
121
    }
122
123
    /**
124
     * Execute an AQL statement and return the boolean result.
125
     *
126
     * @param string|FluentAQL $query
127
     * @param array            $bindings
128
     *
129
     * @return bool
130
     */
131 119
    public function statement($query, $bindings = []): bool
132
    {
133 119
        [$query, $bindings] = $this->handleQueryBuilder(
134 119
            $query,
135
            $bindings
136
        );
137
138 119
        return $this->run($query, $bindings, function ($query, $bindings) {
139 119
            if ($this->pretending()) {
140
                return true;
141
            }
142
143 119
            $statement = $this->arangoClient->prepare($query, $bindings);
144
145 119
            $statement->execute();
146
147 118
            $affectedDocumentCount = $statement->getWritesExecuted();
148 118
            $this->recordsHaveBeenModified($changed = $affectedDocumentCount > 0);
149
150 118
            return $changed;
151 119
        });
152
    }
153
154
    /**
155
     * Run an AQL statement and get the number of rows affected.
156
     *
157
     * @param string|FluentAQL $query
158
     * @param array            $bindings
159
     *
160
     * @return int
161
     */
162 115
    public function affectingStatement($query, $bindings = []): int
163
    {
164 115
        [$query, $bindings] = $this->handleQueryBuilder(
165 115
            $query,
166
            $bindings
167
        );
168
169 115
        return $this->run($query, $bindings, function () use ($query, $bindings) {
170 115
            if ($this->pretending()) {
171
                return 0;
172
            }
173
174
            // For update or delete statements, we want to get the number of rows affected
175
            // by the statement and return that back to the developer. We'll first need
176
            // to execute the statement and get the executed writes from the extra.
177 115
            $statement = $this->arangoClient->prepare($query, $bindings);
178
179 115
            $statement->execute();
180
181 115
            $affectedDocumentCount = $statement->getWritesExecuted();
182
183 115
            $this->recordsHaveBeenModified($affectedDocumentCount > 0);
184
185 115
            return $affectedDocumentCount;
186 115
        });
187
    }
188
189
    /**
190
     * Run a raw, unprepared query against the connection.
191
     *
192
     * @param  string  $query
193
     *
194
     * @return bool
195
     */
196
    public function unprepared($query): bool
197
    {
198
        return $this->run($query, [], function ($query) {
199
            if ($this->pretending()) {
200
                return true;
201
            }
202
203
            $statement = $$this->arangoClient->prepare($query);
204
            $statement->execute();
205
            $affectedDocumentCount = $statement->getWritesExecuted();
206
            $change = $affectedDocumentCount > 0;
207
208
            $this->recordsHaveBeenModified($change);
209
210
            return $change;
211
        });
212
    }
213
214
    /**
215
     * Returns the query execution plan. The query will not be executed.
216
     *
217
     * @param  string  $query
218
     * @param  array<mixed>  $bindings
219
     *
220
     * @return stdClass
221
     */
222 1
    public function explain(string|FluentAQL $query, $bindings = []): stdClass
223
    {
224 1
        [$query, $bindings] = $this->handleQueryBuilder(
225 1
            $query,
226
            $bindings
227
        );
228
229 1
        $statement = $this->arangoClient->prepare($query, $bindings);
230
231 1
        return $statement->explain();
232
    }
233
234
    /**
235
     * Run a select statement against the database.
236
     *
237
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
238
     *
239
     * @param string|FluentAQL $query
240
     * @param array            $bindings
241
     * @param bool             $useReadPdo
242
     * @return mixed
243
     */
244 133
    public function select($query, $bindings = [], $useReadPdo = true)
245
    {
246 133
        return $this->execute($query, $bindings, $useReadPdo);
247
    }
248
249
    /**
250
     * Run an AQL query against the database and return the results.
251
     *
252
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
253
     *
254
     * @param string|FluentAQL $query
255
     * @param array<mixed>|null     $bindings
256
     * @param bool             $useReadPdo
257
     *
258
     * @return mixed
259
     */
260 133
    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

260
    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...
261
    {
262
        // Usage of a separate DB to read date isn't supported at this time
263 133
        $useReadPdo = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $useReadPdo is dead and can be removed.
Loading history...
264
265 133
        [$query, $bindings] = $this->handleQueryBuilder(
266 133
            $query,
267
            $bindings
268
        );
269
270 133
        return $this->run($query, $bindings, function () use ($query, $bindings) {
271 133
            if ($this->pretending()) {
272
                return [];
273
            }
274
275 133
            $statement = $this->arangoClient->prepare($query, $bindings);
276 133
            $statement->execute();
277
278 132
            return $statement->fetchAll();
279 133
        });
280
    }
281
282
    /**
283
     * Get a new query builder instance.
284
     *
285
     * @return QueryBuilder
286
     */
287 107
    public function query(): QueryBuilder
288
    {
289 107
        return new QueryBuilder(
290 107
            $this,
291 107
            $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

291
            /** @scrutinizer ignore-type */ $this->getQueryGrammar(),
Loading history...
292 107
            $this->getPostProcessor()
293
        );
294
    }
295
296
    /**
297
     * Get the collection prefix for the connection.
298
     *
299
     * @return string
300
     */
301
    public function getTablePrefix(): string
302
    {
303
        return $this->tablePrefix;
304
    }
305
306
    /**
307
     * Disconnect from the underlying ArangoDB connection.
308
     *
309
     * @return void
310
     */
311 4
    public function disconnect()
312
    {
313 4
        $this->arangoClient = null;
314 4
    }
315
316
    /**
317
     * Reconnect to the database.
318
     *
319
     * @return void
320
     *
321
     * @throws \LogicException
322
     */
323 1
    public function reconnect()
324
    {
325 1
        if (is_callable($this->reconnector)) {
326 1
            $result = call_user_func($this->reconnector, $this);
327
328 1
            return $result;
329
        }
330
331
        throw new LogicException('Lost connection and no reconnector available.');
332
    }
333
334
    /**
335
     * Reconnect to the database if an ArangoDB connection is missing.
336
     *
337
     * @return void
338
     */
339 134
    protected function reconnectIfMissingConnection()
340
    {
341 134
        if (is_null($this->arangoClient)) {
342
            $this->reconnect();
343
        }
344 134
    }
345
346 137
    public function getArangoClient(): ArangoClient|null
347
    {
348 137
        return $this->arangoClient;
349
    }
350
351
    /**
352
     * @param  string|null  $database
353
     */
354 1
    public function setDatabaseName($database): void
355
    {
356 1
        $this->database = $database;
357 1
        $this->arangoClient->setDatabase($database);
0 ignored issues
show
Bug introduced by
It seems like $database can also be of type null; however, parameter $name of ArangoClient\ArangoClient::setDatabase() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

357
        $this->arangoClient->setDatabase(/** @scrutinizer ignore-type */ $database);
Loading history...
358 1
    }
359
360 2
    public function getDatabaseName(): string
361
    {
362 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...
363
    }
364
365
    /**
366
     * @param  FluentAQL|string  $query
367
     * @param  array  $bindings
368
     * @return array
369
     */
370 135
    private function handleQueryBuilder($query, array $bindings): array
371
    {
372 135
        if ($query instanceof FluentAQL) {
373 107
            $bindings = $query->binds;
374 107
            $query = $query->query;
375
        }
376 135
        return [$query, $bindings];
377
    }
378
379
380 2
    public static function aqb(): ArangoQueryBuilder
381
    {
382 2
        return new ArangoQueryBuilder();
383
    }
384
}
385