Completed
Push — master ( 0f46ff...89eb26 )
by yuuki
11s
created

CouchbaseConnection::select()   A

Complexity

Conditions 4
Paths 1

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 10
nc 1
nop 3
dl 0
loc 15
ccs 9
cts 9
cp 1
crap 4
rs 9.2
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
8
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
9
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
10
 * THE SOFTWARE.
11
 */
12
13
namespace Ytake\LaravelCouchbase\Database;
14
15
use Closure;
16
use CouchbaseBucket;
17
use Illuminate\Database\Connection;
18
use Ytake\LaravelCouchbase\Query\View;
19
use Ytake\LaravelCouchbase\Schema\Builder;
20
use Ytake\LaravelCouchbase\Query\Grammar;
21
use Ytake\LaravelCouchbase\Query\Processor;
22
use Ytake\LaravelCouchbase\Events\QueryPrepared;
23
use Ytake\LaravelCouchbase\Events\ResultReturning;
24
use Ytake\LaravelCouchbase\Query\Builder as QueryBuilder;
25
use Ytake\LaravelCouchbase\Exceptions\NotSupportedException;
26
27
/**
28
 * Class CouchbaseConnection.
29
 *
30
 * @author Yuuki Takezawa<[email protected]>
31
 */
32
class CouchbaseConnection extends Connection
33
{
34
    /** @var string */
35
    protected $bucket;
36
37
    /** @var \CouchbaseCluster */
38
    protected $connection;
39
40
    /** @var */
41
    protected $managerUser;
42
43
    /** @var */
44
    protected $managerPassword;
45
46
    /** @var array */
47
    protected $options = [];
48
49
    /** @var int */
50
    protected $fetchMode = 0;
51
52
    /** @var array */
53
    protected $enableN1qlServers = [];
54
55
    /** @var string */
56
    protected $bucketPassword = '';
57
58
    /** @var string[] */
59
    protected $metrics;
60
61
    /** @var int  default consistency */
62
    protected $consistency = \CouchbaseN1qlQuery::NOT_BOUNDED;
63
64
    /** @var string[]  function to handle the retrieval of various properties. */
65
    private $properties = [
66
        'operationTimeout',
67
        'viewTimeout',
68
        'durabilityInterval',
69
        'durabilityTimeout',
70
        'httpTimeout',
71
        'configTimeout',
72
        'configDelay',
73
        'configNodeTimeout',
74
        'htconfigIdleTimeout',
75
    ];
76
77
    /** @var array */
78
    protected $config = [];
79
80
    /**
81
     * @param array $config
82
     */
83 41
    public function __construct(array $config)
84
    {
85 41
        $this->config = $config;
86 41
        $this->getManagedConfigure($config);
87
88 41
        $this->useDefaultQueryGrammar();
89
90 41
        $this->useDefaultPostProcessor();
91 41
    }
92
93
    /**
94
     * @param $password
95
     *
96
     * @return $this
97
     */
98
    public function setBucketPassword($password)
99
    {
100
        $this->bucketPassword = $password;
101
102
        return $this;
103
    }
104
105
    /**
106
     * @param string $name
107
     *
108
     * @return \CouchbaseBucket
109
     *
110
     * @throws \CouchbaseException
111
     */
112 17
    public function openBucket($name)
113
    {
114 17
        return $this->getCouchbase()->openBucket($name, $this->bucketPassword);
115
    }
116
117
    /**
118
     * @return \CouchbaseClusterManager
119
     */
120 2
    public function manager()
121
    {
122 2
        return $this->getCouchbase()->manager($this->managerUser, $this->managerPassword);
123
    }
124
125
    /**
126
     * @param CouchbaseBucket $bucket
127
     *
128
     * @return string[]
129
     */
130 1
    public function getOptions(\CouchbaseBucket $bucket)
131
    {
132 1
        $options = [];
133 1
        foreach ($this->properties as $property) {
134 1
            $options[$property] = $bucket->$property;
135
        }
136
137 1
        return $options;
138
    }
139
140
    /**
141
     * @param CouchbaseBucket $bucket
142
     */
143 8
    protected function registerOption(\CouchbaseBucket $bucket)
144
    {
145 8
        if (count($this->options)) {
146
            foreach ($this->options as $option => $value) {
147
                $bucket->$option = $value;
148
            }
149
        }
150 8
    }
151
152
    /**
153
     * @return Processor
154
     */
155 41
    protected function getDefaultPostProcessor()
156
    {
157 41
        return new Processor();
158
    }
159
160
    /**
161
     * @return Grammar
162
     */
163 41
    protected function getDefaultQueryGrammar()
164
    {
165 41
        return new Grammar();
166
    }
167
168
    /**
169
     * @return Builder|\Illuminate\Database\Schema\Builder
170
     */
171 6
    public function getSchemaBuilder()
172
    {
173 6
        return new Builder($this);
174
    }
175
176
    /**
177
     *
178
     * @param array $config enable(array), options(array), administrator(array), bucket_password(string)
179
     */
180 41
    protected function getManagedConfigure(array $config)
181
    {
182 41
        $this->enableN1qlServers = (isset($config['enables'])) ? $config['enables'] : [];
183 41
        $this->options = (isset($config['options'])) ? $config['options'] : [];
184 41
        $manager = (isset($config['administrator'])) ? $config['administrator'] : null;
185 41
        $this->managerUser = '';
186 41
        $this->managerPassword = '';
187 41
        if (!is_null($manager)) {
188 41
            $this->managerUser = $config['administrator']['user'];
189 41
            $this->managerPassword = $config['administrator']['password'];
190
        }
191 41
        $this->bucketPassword = (isset($config['bucket_password'])) ? $config['bucket_password'] : '';
192 41
    }
193
194
    /**
195
     * @return \CouchbaseCluster
196
     */
197 32
    protected function createConnection()
198
    {
199
        $this->setReconnector(function () {
200
            $this->connection = (new CouchbaseConnector)->connect($this->config);
201
202
            return $this;
203 32
        });
204
205 32
        return (new CouchbaseConnector)->connect($this->config);
206
    }
207
208
    /**
209
     * {@inheritdoc}
210
     */
211
    public function getDriverName()
212
    {
213
        return 'couchbase';
214
    }
215
216
    /**
217
     * @return \CouchbaseCluster
218
     */
219 32
    public function getCouchbase()
220
    {
221 32
        if (is_null($this->connection)) {
222 32
            $this->connection = $this->createConnection();
223
        }
224
225 32
        return $this->connection;
226
    }
227
228
    /**
229
     * @param string $table
230
     *
231
     * @return QueryBuilder
232
     */
233 8
    public function table($table)
234
    {
235 8
        return $this->bucket($table)->query()->from($table);
236
    }
237
238
    /**
239
     * @param int      $consistency
240
     * @param callable $callback
241
     *
242
     * @return mixed
243
     */
244 1
    public function callableConsistency($consistency, callable $callback)
245
    {
246 1
        $clone = clone $this;
247 1
        $clone->consistency = $consistency;
248
249 1
        return call_user_func_array($callback, [$clone]);
250
    }
251
252
    /**
253
     * @param int $consistency
254
     *
255
     * @return $this
256
     */
257
    public function consistency($consistency)
258
    {
259
        $this->consistency = $consistency;
260
261
        return $this;
262
    }
263
264
    /**
265
     * @param string $bucket
266
     *
267
     * @return $this
268
     */
269 8
    public function bucket($bucket)
270
    {
271 8
        $this->bucket = $bucket;
272
273 8
        return $this;
274
    }
275
276
    /**
277
     * @param \CouchbaseN1qlQuery $query
278
     *
279
     * @return mixed
280
     */
281 8
    protected function executeQuery(\CouchbaseN1qlQuery $query)
282
    {
283 8
        $bucket = $this->openBucket($this->bucket);
284 8
        $this->registerOption($bucket);
285 8
        $this->firePreparedQuery($query);
286 8
        $result = $bucket->query($query);
287 8
        $this->fireReturning($result);
288
289 8
        return $result;
290
    }
291
292
    /**
293
     * {@inheritdoc}
294
     */
295 4
    public function select($query, $bindings = [], $useReadPdo = true)
296
    {
297
        return $this->run($query, $bindings, function ($me, $query, $bindings) {
298 4
            if ($me->pretending()) {
299
                return [];
300
            }
301 4
            $query = \CouchbaseN1qlQuery::fromString($query);
302 4
            $query->consistency($this->consistency);
303 4
            $query->positionalParams($bindings);
304 4
            $result = $this->executeQuery($query);
305 4
            $this->metrics = (isset($result->metrics)) ? $result->metrics : [];
306
307 4
            return (isset($result->rows)) ? $result->rows : [];
308 4
        });
309
    }
310
311
    /**
312
     * @param string $query
313
     * @param array  $bindings
314
     *
315
     * @return int|mixed
316
     */
317 4
    public function insert($query, $bindings = [])
318
    {
319 4
        return $this->affectingStatement($query, $bindings);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->affectingS...ent($query, $bindings); (integer) is incompatible with the return type declared by the interface Illuminate\Database\ConnectionInterface::insert of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
320
    }
321
322
    /**
323
     * {@inheritdoc}
324
     */
325 5 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...
326
    {
327
        return $this->run($query, $bindings, function ($me, $query, $bindings) {
328 5
            if ($me->pretending()) {
329
                return 0;
330
            }
331 5
            $query = \CouchbaseN1qlQuery::fromString($query);
332 5
            $query->consistency($this->consistency);
333 5
            $query->namedParams(['parameters' => $bindings]);
334 5
            $result = $this->executeQuery($query);
335 5
            $this->metrics = (isset($result->metrics)) ? $result->metrics : [];
336
337 5
            return (isset($result->rows[0])) ? $result->rows[0] : false;
338 5
        });
339
    }
340
341
    /**
342
     * @param       $query
343
     * @param array $bindings
344
     *
345
     * @return mixed
346
     */
347 View Code Duplication
    public function positionalStatement($query, array $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...
348
    {
349 5
        return $this->run($query, $bindings, function ($me, $query, $bindings) {
350 5
            if ($me->pretending()) {
351
                return 0;
352
            }
353 5
            $query = \CouchbaseN1qlQuery::fromString($query);
354 5
            $query->consistency($this->consistency);
355 5
            $query->positionalParams($bindings);
356 5
            $result = $this->executeQuery($query);
357 5
            $this->metrics = (isset($result->metrics)) ? $result->metrics : [];
358
359 5
            return (isset($result->rows[0])) ? $result->rows[0] : false;
360 5
        });
361
    }
362
363
    /**
364
     * {@inheritdoc}
365
     */
366 1
    public function transaction(Closure $callback)
367
    {
368 1
        throw new NotSupportedException(__METHOD__);
369
    }
370
371
    /**
372
     * {@inheritdoc}
373
     */
374 1
    public function beginTransaction()
375
    {
376 1
        throw new NotSupportedException(__METHOD__);
377
    }
378
379
    /**
380
     * {@inheritdoc}
381
     */
382 1
    public function commit()
383
    {
384 1
        throw new NotSupportedException(__METHOD__);
385
    }
386
387
    /**
388
     * {@inheritdoc}
389
     */
390 1
    public function rollBack()
391
    {
392 1
        throw new NotSupportedException(__METHOD__);
393
    }
394
395
    /**
396
     * {@inheritdoc}
397
     */
398 8
    protected function reconnectIfMissingConnection()
399
    {
400 8
        if (is_null($this->connection)) {
401 6
            $this->reconnect();
402
        }
403 8
    }
404
405
    /**
406
     * {@inheritdoc}
407
     */
408 6
    public function disconnect()
409
    {
410 6
        $this->connection = null;
411 6
    }
412
413
    /**
414
     * @param CouchbaseBucket $bucket
415
     *
416
     * @return CouchbaseBucket
417
     */
418
    protected function enableN1ql(CouchbaseBucket $bucket)
419
    {
420
        if (!count($this->enableN1qlServers)) {
421
            return $bucket;
422
        }
423
        $bucket->enableN1ql($this->enableN1qlServers);
424
425
        return $bucket;
426
    }
427
428
    /**
429
     * N1QL upsert query.
430
     *
431
     * @param string $query
432
     * @param array  $bindings
433
     *
434
     * @return int
435
     */
436 1
    public function upsert($query, $bindings = [])
437
    {
438 1
        return $this->affectingStatement($query, $bindings);
439
    }
440
441
    /**
442
     * Get a new query builder instance.
443
     *
444
     * @return QueryBuilder
445
     */
446 8
    public function query()
447
    {
448 8
        return new QueryBuilder(
449 8
            $this, $this->getQueryGrammar(), $this->getPostProcessor()
450
        );
451
    }
452
453
    /**
454
     * @param string|null $bucket
455
     *
456
     * @return View
457
     */
458 1
    public function view($bucket = null)
459
    {
460 1
        $bucket = is_null($bucket) ? $this->bucket : $bucket;
461
462 1
        return new View($this->openBucket($bucket), $this->events);
463
    }
464
465
    /**
466
     * Run an update statement against the database.
467
     *
468
     * @param string $query
469
     * @param array  $bindings
470
     *
471
     * @return int|\stdClass
472
     */
473 1
    public function update($query, $bindings = [])
474
    {
475 1
        return $this->positionalStatement($query, $bindings);
476
    }
477
478
    /**
479
     * Run a delete statement against the database.
480
     *
481
     * @param string $query
482
     * @param array  $bindings
483
     *
484
     * @return int|\stdClass
485
     */
486 5
    public function delete($query, $bindings = [])
487
    {
488 5
        return $this->positionalStatement($query, $bindings);
489
    }
490
491
    /**
492
     * @return \string[]
493
     */
494 1
    public function metrics()
495
    {
496 1
        return $this->metrics;
497
    }
498
499
    /**
500
     * @param \CouchbaseN1qlQuery $queryObject
501
     */
502 8
    protected function firePreparedQuery(\CouchbaseN1qlQuery $queryObject)
503
    {
504 8
        if (isset($this->events)) {
505 8
            $this->events->fire(new QueryPrepared($queryObject));
506
        }
507 8
    }
508
509
    /**
510
     * @param mixed $returning
511
     */
512 8
    protected function fireReturning($returning)
513
    {
514 8
        if (isset($this->events)) {
515 8
            $this->events->fire(new ResultReturning($returning));
516
        }
517 8
    }
518
}
519