Passed
Push — next ( 9707a2...aa8376 )
by Bas
03:34
created

Connection::cursor()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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

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

253
    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...
254
    {
255
        // Usage of a separate DB to read date isn't supported at this time
256 113
        $useReadPdo = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $useReadPdo is dead and can be removed.
Loading history...
257
258 113
        if ($query instanceof FluentAQL) {
259 75
            $bindings = $query->binds;
260 75
            $query = $query->query;
261
        }
262
263 113
        return $this->run($query, $bindings, function () use ($query, $bindings) {
264 113
            if ($this->pretending()) {
265
                return [];
266
            }
267
268 113
            $statement = $this->arangoClient->prepare($query, $bindings);
0 ignored issues
show
Bug introduced by
It seems like $query can also be of type null; however, parameter $query of ArangoClient\ArangoClient::prepare() 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

268
            $statement = $this->arangoClient->prepare(/** @scrutinizer ignore-type */ $query, $bindings);
Loading history...
269 113
            $statement->execute();
270
271 112
            return $statement->fetchAll();
272 113
        });
273
    }
274
275
    /**
276
     * Get a new query builder instance.
277
     *
278
     * @return QueryBuilder
279
     */
280 95
    public function query(): QueryBuilder
281
    {
282 95
        return new QueryBuilder(
283 95
            $this,
284 95
            $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

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