Test Failed
Pull Request — master (#4)
by
unknown
02:59
created

Connection::select()   B

Complexity

Conditions 4
Paths 1

Size

Total Lines 34
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 19
nc 1
nop 3
dl 0
loc 34
rs 8.5806
c 0
b 0
f 0
ccs 7
cts 7
cp 1
crap 4
1
<?php
2
/**
3
 * @author Donii Sergii <[email protected]>
4
 */
5
6
namespace sonrac\Arango;
7
8
use ArangoDBClient\CollectionHandler as ArangoDBCollectionHandler;
9
use ArangoDBClient\Connection as ArangoDBConnection;
10
use ArangoDBClient\ConnectionOptions as ArangoDBConnectionOptions;
11
use ArangoDBClient\Document;
12
use ArangoDBClient\Exception as ArangoException;
13
use ArangoDBClient\Statement;
14
use ArangoDBClient\UpdatePolicy as ArangoDBUpdatePolicy;
15
use Illuminate\Database\Connection as IlluminateConnection;
16
use sonrac\Arango\Query\Grammars\Grammar;
17
use sonrac\Arango\Query\Processors\Processor;
18
use sonrac\Arango\Query\QueryBuilder;
19
20
/**
21
 * Class Connection.
22
 * Arango connection.
23
 *
24
 * @author  Donii Sergii <[email protected]>
25
 */
26
class Connection extends IlluminateConnection
27
{
28
    /**
29
     * Arango.DB Query Builder
30
     *
31
     * @var
32
     *
33
     * @author Donii Sergii <[email protected]>
34
     */
35
    protected $db;
36
37
    protected $database = '_system';
38
39
    /**
40
     * Arango.DB connection
41
     *
42
     * @var \ArangoDBClient\Connection
43
     *
44
     * @author Donii Sergii <[email protected]>
45
     */
46
    protected $arangoConnection;
47
48
    /**
49
     * Connection constructor.
50 11
     *
51
     * @param array $config Connection options
52 11
     *
53
     * @throws ArangoException
54 11
     *
55
     * @author Donii Sergii <[email protected]>
56 11
     */
57 11
    public function __construct(array $config = [])
58
    {
59
        $this->config = $config;
60
61
        $this->arangoConnection = $this->createConnection();
62
63
        $this->db = new ArangoDBCollectionHandler($this->arangoConnection);
64
65
        // We need to initialize a query grammar and the query post processors
66 1
        // which are both very important parts of the database abstractions
67
        // so we initialize these to their default values while starting.
68 1
        $this->useDefaultQueryGrammar();
69
70
        $this->useDefaultPostProcessor();
71
    }
72
73
    /**
74
     * Send AQL request to ArangoDB and return response with flat array
75
     *
76
     * @param string $query
77
     * @param array $bindings
78 1
     * @param bool $useReadPdo
79
     * @return mixed
80 1
     */
81
    public function select($query, $bindings = [], $useReadPdo = true)
82
    {
83
        return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
84
            if ($this->pretending()) {
85
                return [];
86
            }
87
88
            $query = $this->prepareBindingsInQuery($query);
89
            $options = [
90
                'query' => $query,
91
                'count' => true,
92 11
                'batchSize' => 1000,
93
                'sanitize'  => true,
94 11
            ];
95
96 11
            if (count($bindings) > 0) {
97
                $options['bindVars'] = $this->prepareBindings($bindings);
98 11
                var_dump($options['bindVars']);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($options['bindVars']); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
99
            }
100
101
            $statement = new Statement($this->getArangoClient(), $options);
102
103
            $cursor = $statement->execute();
104
105
            $resultingDocuments = [];
106
107
            foreach ($cursor as $key => $document) {
0 ignored issues
show
Bug introduced by
The expression $cursor of type object<ArangoDBClient\Cursor>|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
108 11
                /* @var Document $document */
109
                $resultingDocuments[$key] = $document->getAll();
110 11
            }
111
112 11
            return $resultingDocuments;
113
        });
114
    }
115
    /**
116
     * {@inheritdoc}
117
     */
118
    public function query()
119
    {
120
        return new QueryBuilder(
121
            $this, $this->getQueryGrammar(), $this->getPostProcessor()
122 11
        );
123
    }
124 11
125
    /**
126
     * {@inheritdoc}
127
     */
128 View Code Duplication
    public function statement($query, $bindings = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
129
    {
130
        return $this->run($query, $bindings, function ($query, $bindings) {
131
            if ($this->pretending()) {
132
                return true;
133
            }
134 11
135
            $query = $this->prepareBindingsInQuery($query);
136 11
            $options = [
137
                'query' => $query,
138
                'count' => true,
139
                'batchSize' => 1000,
140
                'sanitize'  => true,
141
            ];
142
143
            if (count($bindings) > 0) {
144
                $options['bindVars'] = $this->prepareBindings($bindings);
145
            }
146 11
147
            $statement = new Statement($this->getArangoClient(), $options);
148 11
149
            return $statement->execute();
150
        });
151
    }
152
153
    /**
154
     * {@inheritdoc}
155
     */
156 View Code Duplication
    public function affectingStatement($query, $bindings = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
157
    {
158 11
        return $this->run($query, $bindings, function ($query, $bindings) {
159
            $query = $this->prepareBindingsInQuery($query);
160 11
161
            $options = [
162
                'query' => $query,
163
                'count' => true,
164
                'batchSize' => 1000,
165
                'sanitize'  => true,
166
            ];
167
168
            if (count($bindings) > 0) {
169
                $options['bindVars'] = $this->prepareBindings($bindings);
170 11
            }
171
172 11
            $statement = new Statement($this->getArangoClient(), $options);
173
174
            return $statement->execute();
175
        });
176
    }
177
178
    /**
179
     * Get Arango.DB
180
     *
181
     * @return mixed
182 11
     *
183
     * @author Donii Sergii <[email protected]>
184 11
     */
185
    public function getArangoDB()
186
    {
187
        return $this->db;
188
    }
189
190
    /**
191
     * Get Arango.DB client
192
     *
193
     * @return \ArangoDBClient\Connection
194 11
     *
195
     * @author Donii Sergii <[email protected]>
196 11
     */
197
    public function getArangoClient()
198
    {
199
        return $this->arangoConnection;
200
    }
201
202
    /**
203
     * Create new arango.db connection.
204
     *
205
     * @param array $config Config
206 11
     *
207
     * @return ArangoDBConnection
208 11
     *
209
     * @throws \ArangoDBClient\Exception
210
     *
211
     * @author Donii Sergii <[email protected]>
212
     */
213
    public function createConnection(array $config = [])
214
    {
215
        $config = $this->config + $config;
216
217
        ArangoException::enableLogging();
218 11
219
        return new ArangoDBConnection($this->getMainConnectionOption() + $config);
220 11
    }
221
222
    /**
223
     * Get database name.
224
     *
225
     * @return string
226
     *
227
     * @author Donii Sergii <[email protected]>
228
     */
229
    public function getDatabaseName()
230
    {
231
        $this->database = $this->getOption(ArangoDBConnectionOptions::OPTION_DATABASE, $this->database);
232
233 11
        return parent::getDatabaseName();
234
    }
235 11
236 11
    /**
237
     * Get arango.db endpoint.
238
     *
239 11
     * @return string
240
     *
241
     * @author Donii Sergii <[email protected]>
242
     */
243
    public function getEndPoint()
244
    {
245
        return $this->getOption(ArangoDBConnectionOptions::OPTION_ENDPOINT, 'tcp://127.0.0.1:8529');
246
    }
247
248
    /**
249 11
     * Get auth type
250
     *
251
     * @return string
252
     *
253 11
     * @author Donii Sergii <[email protected]>
254
     */
255 11
    public function getAuthType()
256
    {
257 11
        return $this->getOption(ArangoDBConnectionOptions::OPTION_AUTH_TYPE, 'Basic');
258
    }
259 11
260
    /**
261 11
     * Get auth type
262
     *
263 11
     * @return string
264
     *
265 11
     * @author Donii Sergii <[email protected]>
266
     */
267 11
    public function getAuthUser()
268
    {
269 11
        return $this->getOption(ArangoDBConnectionOptions::OPTION_AUTH_USER, 'root');
270
    }
271 11
272
    /**
273
     * Get auth type
274
     *
275
     * @return string
276
     *
277
     * @author Donii Sergii <[email protected]>
278
     */
279
    public function getAuthPassword()
280
    {
281
        return $this->getOption(ArangoDBConnectionOptions::OPTION_AUTH_PASSWD, '');
282
    }
283
284
    /**
285
     * Get connection option
286
     *
287
     * @return string
288
     *
289
     * @author Donii Sergii <[email protected]>
290
     */
291
    public function getConnectionOption()
292
    {
293
        return $this->getOption(ArangoDBConnectionOptions::OPTION_CONNECTION, 'Keep-Alive');
294
    }
295
296
    /**
297
     * Get timeout option
298
     *
299
     * @return int
300
     *
301
     * @author Donii Sergii <[email protected]>
302
     */
303
    public function getTimeout()
304
    {
305
        return $this->getOption(ArangoDBConnectionOptions::OPTION_TIMEOUT, 3);
306
    }
307
308
    /**
309
     * Get reconnect option
310
     *
311
     * @return bool
312
     *
313
     * @author Donii Sergii <[email protected]>
314
     */
315
    public function getReconnect()
316
    {
317
        return $this->getOption(ArangoDBConnectionOptions::OPTION_RECONNECT, true);
318
    }
319
320
    /**
321
     * Get create option
322
     *
323
     * @return mixed
324
     *
325
     * @author Donii Sergii <[email protected]>
326
     */
327
    public function getCreate()
328
    {
329
        return $this->getOption(ArangoDBConnectionOptions::OPTION_CREATE, true);
330
    }
331
332
    /**
333
     * Get update policy option
334
     *
335
     * @return string
336
     *
337
     * @author Donii Sergii <[email protected]>
338
     */
339
    public function getUpdatePolicy()
340
    {
341
        return $this->getOption(ArangoDBConnectionOptions::OPTION_UPDATE_POLICY, ArangoDBUpdatePolicy::LAST);
342
    }
343
344
    public function getDefaultQueryGrammar()
345
    {
346
        return new Grammar();
347
    }
348
349
    public function getDefaultPostProcessor()
350
    {
351
        return new Processor();
352
    }
353
354
    protected function prepareBindingsInQuery($query)
355
    {
356
        $query = explode('?', $query);
357
        $result = '';
358
        foreach ($query as $index => $part) {
359
            if ($index === count($query) - 1) {
360
                $result .= $part;
361
                continue;
362
            }
363
            $result .= $part.'@B'.($index + 1);
364
        }
365
        return $result;
366
    }
367
368
    /**
369
     * Reconnect to the database if a PDO connection is missing.
370
     *
371
     * @throws \ArangoDBClient\Exception
372
     * @return void
373
     */
374
    protected function reconnectIfMissingConnection()
375
    {
376
        if (is_null($this->arangoConnection)) {
377
            $this->arangoConnection = $this->createConnection();
378
        }
379
    }
380
381
    /**
382
     * Get option value
383
     *
384
     * @param string $optionName
385
     * @param mixed  $default
386
     *
387
     * @return mixed
388
     *
389
     * @author Donii Sergii <[email protected]>
390
     */
391
    private function getOption($optionName, $default)
392
    {
393
        if (!isset($this->config[$optionName])) {
394
            $this->config[$optionName] = $default;
395
        }
396
397
        return $this->config[$optionName];
398
    }
399
400
    /**
401
     * Get main connection option
402
     *
403
     * @return array
404
     *
405
     * @author Donii Sergii <[email protected]>
406
     */
407
    private function getMainConnectionOption()
408
    {
409
        return [
410
            // database name
411
            ArangoDBConnectionOptions::OPTION_DATABASE      => $this->getDatabaseName(),
412
            // server endpoint to connect to
413
            ArangoDBConnectionOptions::OPTION_ENDPOINT      => $this->getEndPoint(),
414
            // authorization type to use (currently supported: 'Basic')
415
            ArangoDBConnectionOptions::OPTION_AUTH_TYPE     => $this->getAuthType(),
416
            // user for basic authorization
417
            ArangoDBConnectionOptions::OPTION_AUTH_USER     => $this->getAuthUser(),
418
            // password for basic authorization
419
            ArangoDBConnectionOptions::OPTION_AUTH_PASSWD   => $this->getAuthPassword(),
420
            // connection persistence on server. can use either 'Close' (one-time connections) or 'Keep-Alive' (re-used connections)
421
            ArangoDBConnectionOptions::OPTION_CONNECTION    => $this->getConnectionOption(),
422
            // connect timeout in seconds
423
            ArangoDBConnectionOptions::OPTION_TIMEOUT       => $this->getTimeout(),
424
            // whether or not to reconnect when a keep-alive connection has timed out on server
425
            ArangoDBConnectionOptions::OPTION_RECONNECT     => $this->getReconnect(),
426
            // optionally create new collections when inserting documents
427
            ArangoDBConnectionOptions::OPTION_CREATE        => $this->getCreate(),
428
            // optionally create new collections when inserting documents
429
            ArangoDBConnectionOptions::OPTION_UPDATE_POLICY => $this->getUpdatePolicy(),
430
        ];
431
    }
432
}
433