Passed
Push — fix/updated_at ( 18809c...230688 )
by Bas
15:08 queued 12:05
created

Connection::statement()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2.003

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 11
c 3
b 1
f 0
dl 0
loc 20
ccs 10
cts 11
cp 0.9091
rs 9.9
cc 2
nc 1
nop 2
crap 2.003
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 136
    public function __construct($config = [])
42
    {
43 136
        $this->config = $config;
44
45 136
        $this->database = (isset($this->config['database'])) ? $this->config['database'] : null;
46 136
        $this->tablePrefix = isset($this->config['tablePrefix']) ? $this->config['tablePrefix'] : null;
47
48
        // activate and set the database client connection
49 136
        $this->arangoClient = new ArangoClient($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 136
        $this->useDefaultQueryGrammar();
55
56 136
        $this->useDefaultPostProcessor();
57 136
    }
58
59
    /**
60
     * Get a schema builder instance for the connection.
61
     *
62
     * @return SchemaBuilder
63
     */
64 136
    public function getSchemaBuilder(): SchemaBuilder
65
    {
66 136
        if (is_null($this->schemaGrammar)) {
67
            $this->useDefaultSchemaGrammar();
68
        }
69
70 136
        return new SchemaBuilder($this);
71
    }
72
73
    /**
74
     * Get the default query grammar instance.
75
     *
76
     * @return QueryGrammar
77
     */
78 136
    protected function getDefaultQueryGrammar(): QueryGrammar
79
    {
80 136
        return new QueryGrammar();
81
    }
82
83
    /**
84
     * Get the default post processor instance.
85
     *
86
     * @return Processor
87
     */
88 136
    protected function getDefaultPostProcessor(): Processor
89
    {
90 136
        return new Processor();
91
    }
92
93
    /**
94
     * Run a select statement against the database and returns a generator.
95
     * ($useReadPdo is a dummy to adhere to the interface).
96
     *
97
     * @param  string  $query
98
     * @param  array  $bindings
99
     * @param  bool|null  $useReadPdo
100
     * @return Iterator|null
101
     */
102
    public function cursor($query, $bindings = [], $useReadPdo = null): ?Iterator
103
    {
104
        // Usage of a separate DB to read date isn't supported at this time
105
        $useReadPdo = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $useReadPdo is dead and can be removed.
Loading history...
106
107
        return $this->run($query, $bindings, function ($query, $bindings) {
108
            if ($this->pretending()) {
109
                return [];
110
            }
111
112
            $statement = $this->arangoClient->prepare($query, $bindings);
113
114
            return $statement->execute();
115
        });
116
    }
117
118
    /**
119
     * Execute an AQL statement and return the boolean result.
120
     *
121
     * @param string|FluentAQL $query
122
     * @param array            $bindings
123
     *
124
     * @return bool
125
     */
126 136
    public function statement($query, $bindings = []): bool
127
    {
128 136
        [$query, $bindings] = $this->handleQueryBuilder(
129 136
            $query,
130
            $bindings
131
        );
132
133 136
        return $this->run($query, $bindings, function ($query, $bindings) {
134 136
            if ($this->pretending()) {
135
                return true;
136
            }
137
138 136
            $statement = $this->arangoClient->prepare($query, $bindings);
139
140 136
            $statement->execute();
141
142 136
            $affectedDocumentCount = $statement->getWritesExecuted();
143 136
            $this->recordsHaveBeenModified($changed = $affectedDocumentCount > 0);
144
145 136
            return $changed;
146 136
        });
147
    }
148
149
    /**
150
     * Run an AQL statement and get the number of rows affected.
151
     *
152
     * @param string|FluentAQL $query
153
     * @param array            $bindings
154
     *
155
     * @return int
156
     */
157 20
    public function affectingStatement($query, $bindings = []): int
158
    {
159 20
        [$query, $bindings] = $this->handleQueryBuilder(
160 20
            $query,
161
            $bindings
162
        );
163
164 20
        return $this->run($query, $bindings, function () use ($query, $bindings) {
165 20
            if ($this->pretending()) {
166
                return 0;
167
            }
168
169
            // For update or delete statements, we want to get the number of rows affected
170
            // by the statement and return that back to the developer. We'll first need
171
            // to execute the statement and get the executed writes from the extra.
172 20
            $statement = $this->arangoClient->prepare($query, $bindings);
173
174 20
            $statement->execute();
175
176 20
            $affectedDocumentCount = $statement->getWritesExecuted();
177
178 20
            $this->recordsHaveBeenModified($affectedDocumentCount > 0);
179
180 20
            return $affectedDocumentCount;
181 20
        });
182
    }
183
184
    /**
185
     * Run a raw, unprepared query against the connection.
186
     *
187
     * @param  string  $query
188
     *
189
     * @return bool
190
     */
191
    public function unprepared($query): bool
192
    {
193
        return $this->run($query, [], function ($query) {
194
            if ($this->pretending()) {
195
                return true;
196
            }
197
198
            $statement = $$this->arangoClient->prepare($query);
199
            $statement->execute();
200
            $affectedDocumentCount = $statement->getWritesExecuted();
201
            $change = $affectedDocumentCount > 0;
202
203
            $this->recordsHaveBeenModified($change);
204
205
            return $change;
206
        });
207
    }
208
209
    /**
210
     * Returns the query execution plan. The query will not be executed.
211
     *
212
     * @param  string  $query
213
     * @param  array  $bindings
214
     *
215
     * @return array
216
     */
217 1
    public function explain(string $query, $bindings = []): array
218
    {
219 1
        $statement = $this->arangoClient->prepare($query, $bindings);
220
221 1
        return $statement->explain();
222
    }
223
224
    /**
225
     * Run a select statement against the database.
226
     *
227
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
228
     *
229
     * @param string|FluentAQL $query
230
     * @param array            $bindings
231
     * @param bool             $useReadPdo
232
     * @return mixed
233
     */
234 136
    public function select($query, $bindings = [], $useReadPdo = true)
235
    {
236 136
        return $this->execute($query, $bindings, $useReadPdo);
237
    }
238
239
    /**
240
     * Run an AQL query against the database and return the results.
241
     *
242
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
243
     *
244
     * @param string|FluentAQL $query
245
     * @param array<mixed>|null     $bindings
246
     * @param bool             $useReadPdo
247
     *
248
     * @return mixed
249
     */
250 136
    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

250
    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...
251
    {
252
        // Usage of a separate DB to read date isn't supported at this time
253 136
        $useReadPdo = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $useReadPdo is dead and can be removed.
Loading history...
254
255 136
        if ($query instanceof FluentAQL) {
256 64
            $bindings = $query->binds;
257 64
            $query = $query->query;
258
        }
259
260 136
        return $this->run($query, $bindings, function () use ($query, $bindings) {
261 136
            if ($this->pretending()) {
262
                return [];
263
            }
264
265 136
            $statement = $this->arangoClient->prepare($query, $bindings);
266 136
            $statement->execute();
267
268 136
            return $statement->fetchAll();
269 136
        });
270
    }
271
272
    /**
273
     * Get a new query builder instance.
274
     *
275
     * @return QueryBuilder
276
     */
277 75
    public function query(): QueryBuilder
278
    {
279 75
        return new QueryBuilder(
280 75
            $this,
281 75
            $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

281
            /** @scrutinizer ignore-type */ $this->getQueryGrammar(),
Loading history...
282 75
            $this->getPostProcessor()
283
        );
284
    }
285
286
    /**
287
     * Get the collection prefix for the connection.
288
     *
289
     * @return string
290
     */
291
    public function getTablePrefix(): string
292
    {
293
        return $this->tablePrefix;
294
    }
295
296
    /**
297
     * Disconnect from the underlying ArangoDB connection.
298
     *
299
     * @return void
300
     */
301
    public function disconnect()
302
    {
303
        $this->transactions = 0;
304
305
        $this->arangoClient = null;
306
    }
307
308
    /**
309
     * Reconnect to the database if a Arango connection is missing.
310
     *
311
     * @return void
312
     */
313 136
    protected function reconnectIfMissingConnection()
314
    {
315 136
        if (is_null($this->arangoClient)) {
316
            $this->reconnect();
317
        }
318 136
    }
319
320 136
    public function getArangoClient(): ArangoClient
321
    {
322 136
        return $this->arangoClient;
323
    }
324
325
    /**
326
     * @param  string  $database
327
     */
328 136
    public function setDatabaseName($database): void
329
    {
330 136
        $this->database = $database;
331 136
        $this->arangoClient->setDatabase($database);
332 136
    }
333
334 2
    public function getDatabaseName(): string
335
    {
336 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...
337
    }
338
339
    /**
340
     * @param  FluentAQL|string  $query
341
     * @param  array  $bindings
342
     * @return array
343
     */
344 136
    private function handleQueryBuilder($query, array $bindings): array
345
    {
346 136
        if ($query instanceof FluentAQL) {
347 73
            $bindings = $query->binds;
348 73
            $query = $query->query;
349
        }
350 136
        return [$query, $bindings];
351
    }
352
}
353