Failed Conditions
Push — master ( b620eb...ef1328 )
by Bas
21:02 queued 15:51
created

Connection::explain()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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

296
    public function execute($query, $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...
297
    {
298
        // Usage of a separate DB to read date isn't supported at this time
299 121
        $useReadPdo = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $useReadPdo is dead and can be removed.
Loading history...
300
301 121
        if ($query instanceof FluentAQL) {
302 51
            $bindings = $query->binds;
303 51
            $query = $query->query;
304
        }
305
306 121
        return $this->run($query, $bindings, function () use ($query, $bindings) {
307 121
            if ($this->pretending()) {
308
                return [];
309
            }
310
311 121
            $statement = $this->newArangoStatement($query, $bindings);
312 121
            $cursor = $statement->execute();
313
314 121
            return $cursor->getAll();
315 121
        });
316
    }
317
318
    /**
319
     * Get a new query builder instance.
320
     *
321
     * @return QueryBuilder
322
     */
323 61
    public function query()
324
    {
325 61
        return new QueryBuilder(
326 61
            $this,
327 61
            $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

327
            /** @scrutinizer ignore-type */ $this->getQueryGrammar(),
Loading history...
328 61
            $this->getPostProcessor()
329
        );
330
    }
331
332
    /**
333
     * Get the collection prefix for the connection.
334
     *
335
     * @return string
336
     */
337
    public function getTablePrefix()
338
    {
339
        return $this->tablePrefix;
340
    }
341
342
    /**
343
     * Disconnect from the underlying ArangoDB connection.
344
     *
345
     * @return void
346
     */
347
    public function disconnect()
348
    {
349
        $this->transactions = 0;
350
351
        $this->arangoConnection = null;
352
    }
353
354
    /**
355
     * Reconnect to the database if a Arango connection is missing.
356
     *
357
     * @return void
358
     */
359 121
    protected function reconnectIfMissingConnection()
360
    {
361 121
        if (is_null($this->arangoConnection)) {
362
            $this->reconnect();
363
        }
364 121
    }
365
366 121
    public function getArangoConnection()
367
    {
368 121
        return $this->arangoConnection;
369
    }
370
371
    /**
372
     * @param $query
373
     * @param $bindings
374
     * @return Statement
375
     * @throws Exception
376
     */
377 121
    protected function newArangoStatement($query, $bindings): Statement
378
    {
379 121
        $data = ['query' => $query, 'bindVars' => $bindings];
380
381 121
        if ($this->transactionLevel() > 0) {
382 3
            $data['transaction'] = $this->arangoTransaction;
383
        }
384
385 121
        $statement = new Statement($this->arangoConnection, $data);
386 121
        $statement->setDocumentClass(Document::class);
387
388 121
        return $statement;
389
    }
390
391
392 121
    public function setDatabaseName($database)
393
    {
394 121
        $this->database = $database;
395 121
        $this->arangoConnection->setDatabase($database);
396 121
    }
397
398 1
    public function getDatabaseName()
399
    {
400 1
        return $this->database;
401
    }
402
403
    /**
404
     * @param  FluentAQL|string  $query
405
     * @param  array  $bindings
406
     * @return array
407
     */
408 121
    private function handleQueryBuilder($query, array $bindings)
409
    {
410 121
        if ($query instanceof FluentAQL) {
411 59
            $bindings = $query->binds;
412 59
            $query = $query->query;
413
        }
414 121
        return [$query, $bindings];
415
    }
416
}
417