Completed
Pull Request — master (#29)
by yuuki
02:25
created

CouchbaseConnection::positionalStatement()   A

Complexity

Conditions 4
Paths 1

Size

Total Lines 15
Code Lines 10

Duplication

Lines 15
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 10
nc 1
nop 2
dl 15
loc 15
rs 9.2
c 0
b 0
f 0
ccs 0
cts 0
cp 0
crap 20
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
    public function __construct(array $config)
84
    {
85
        $this->config = $config;
86 1
        $this->getManagedConfigure($config);
87
88 1
        $this->useDefaultQueryGrammar();
89 1
90
        $this->useDefaultPostProcessor();
91 1
    }
92
93 1
    /**
94 1
     * @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
    public function openBucket($name)
113
    {
114
        return $this->getCouchbase()->openBucket($name, $this->bucketPassword);
115 1
    }
116
117 1
    /**
118
     * @return \CouchbaseClusterManager
119
     */
120
    public function manager()
121
    {
122
        return $this->getCouchbase()->manager($this->managerUser, $this->managerPassword);
123
    }
124
125
    /**
126
     * @param CouchbaseBucket $bucket
127
     *
128
     * @return string[]
129
     */
130
    public function getOptions(\CouchbaseBucket $bucket)
131
    {
132
        $options = [];
133
        foreach ($this->properties as $property) {
134
            $options[$property] = $bucket->$property;
135
        }
136
137
        return $options;
138
    }
139
140
    /**
141
     * @param CouchbaseBucket $bucket
142
     */
143
    protected function registerOption(\CouchbaseBucket $bucket)
144
    {
145
        if (count($this->options)) {
146
            foreach ($this->options as $option => $value) {
147
                $bucket->$option = $value;
148
            }
149
        }
150
    }
151
152
    /**
153
     * @return Processor
154
     */
155
    protected function getDefaultPostProcessor()
156
    {
157
        return new Processor();
158 1
    }
159
160 1
    /**
161
     * @return Grammar
162
     */
163
    protected function getDefaultQueryGrammar()
164
    {
165
        return new Grammar();
166 1
    }
167
168 1
    /**
169
     * @return Builder|\Illuminate\Database\Schema\Builder
170
     */
171
    public function getSchemaBuilder()
172
    {
173
        return new Builder($this);
174
    }
175
176
    /**
177
     *
178
     * @param array $config enable(array), options(array), administrator(array), bucket_password(string)
179
     */
180
    protected function getManagedConfigure(array $config)
181
    {
182
        $this->enableN1qlServers = (isset($config['enables'])) ? $config['enables'] : [];
183 1
        $this->options = (isset($config['options'])) ? $config['options'] : [];
184
        $manager = (isset($config['administrator'])) ? $config['administrator'] : null;
185 1
        $this->managerUser = '';
186 1
        $this->managerPassword = '';
187 1
        if (!is_null($manager)) {
188 1
            $this->managerUser = $config['administrator']['user'];
189 1
            $this->managerPassword = $config['administrator']['password'];
190 1
        }
191 1
        $this->bucketPassword = (isset($config['bucket_password'])) ? $config['bucket_password'] : '';
192 1
    }
193
194 1
    /**
195 1
     * @return \CouchbaseCluster
196
     */
197
    protected function createConnection()
198
    {
199
        $this->setReconnector(function () {
200 1
            $this->connection = (new CouchbaseConnector)->connect($this->config);
201
202
            return $this;
203
        });
204
205
        return (new CouchbaseConnector)->connect($this->config);
206 1
    }
207
208 1
    /**
209
     * {@inheritdoc}
210
     */
211
    public function getDriverName()
212
    {
213
        return 'couchbase';
214
    }
215
216
    /**
217
     * @return \CouchbaseCluster
218
     */
219
    public function getCouchbase()
220
    {
221
        if (is_null($this->connection)) {
222 1
            $this->connection = $this->createConnection();
223
        }
224 1
225 1
        return $this->connection;
226
    }
227
228 1
    /**
229
     * @param string $table
230
     *
231
     * @return QueryBuilder
232
     */
233
    public function table($table)
234
    {
235
        return $this->bucket($table)->query()->from($table);
236
    }
237
238
    /**
239
     * @param int      $consistency
240
     * @param callable $callback
241
     *
242
     * @return mixed
243
     */
244
    public function callableConsistency($consistency, callable $callback)
245
    {
246
        $clone = clone $this;
247
        $clone->consistency = $consistency;
248
249
        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
    public function bucket($bucket)
270
    {
271
        $this->bucket = $bucket;
272
273
        return $this;
274
    }
275
276
    /**
277
     * @param \CouchbaseN1qlQuery $query
278
     *
279
     * @return mixed
280
     */
281
    protected function executeQuery(\CouchbaseN1qlQuery $query)
282
    {
283
        $bucket = $this->openBucket($this->bucket);
284
        $this->registerOption($bucket);
285
        $this->firePreparedQuery($query);
286
        $result = $bucket->query($query);
287
        $this->fireReturning($result);
288
289
        return $result;
290
    }
291
292
    /**
293
     * {@inheritdoc}
294
     */
295
    public function select($query, $bindings = [], $useReadPdo = true)
296
    {
297
        return $this->run($query, $bindings, function ($me, $query, $bindings) {
298
            if ($me->pretending()) {
299
                return [];
300
            }
301
            $query = \CouchbaseN1qlQuery::fromString($query);
302
            $query->consistency($this->consistency);
303
            $query->positionalParams($bindings);
304
            $result = $this->executeQuery($query);
305
            $this->metrics = (isset($result->metrics)) ? $result->metrics : [];
306
307
            return (isset($result->rows)) ? $result->rows : [];
308
        });
309
    }
310
311
    /**
312
     * @param string $query
313
     * @param array  $bindings
314
     *
315
     * @return int|mixed
316
     */
317
    public function insert($query, $bindings = [])
318
    {
319
        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 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
            if ($me->pretending()) {
329
                return 0;
330
            }
331
            $query = \CouchbaseN1qlQuery::fromString($query);
332
            $query->consistency($this->consistency);
333
            $query->namedParams(['parameters' => $bindings]);
334
            $result = $this->executeQuery($query);
335
            $this->metrics = (isset($result->metrics)) ? $result->metrics : [];
336
337
            return (isset($result->rows[0])) ? $result->rows[0] : false;
338
        });
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
        return $this->run($query, $bindings, function ($me, $query, $bindings) {
350
            if ($me->pretending()) {
351
                return 0;
352
            }
353
            $query = \CouchbaseN1qlQuery::fromString($query);
354
            $query->consistency($this->consistency);
355
            $query->positionalParams($bindings);
356
            $result = $this->executeQuery($query);
357
            $this->metrics = (isset($result->metrics)) ? $result->metrics : [];
358
359
            return (isset($result->rows[0])) ? $result->rows[0] : false;
360
        });
361
    }
362
363
    /**
364
     * {@inheritdoc}
365
     */
366
    public function transaction(Closure $callback)
367
    {
368
        throw new NotSupportedException(__METHOD__);
369
    }
370
371
    /**
372
     * {@inheritdoc}
373
     */
374
    public function beginTransaction()
375
    {
376
        throw new NotSupportedException(__METHOD__);
377
    }
378
379
    /**
380
     * {@inheritdoc}
381
     */
382
    public function commit()
383
    {
384
        throw new NotSupportedException(__METHOD__);
385
    }
386
387
    /**
388
     * {@inheritdoc}
389
     */
390
    public function rollBack()
391
    {
392
        throw new NotSupportedException(__METHOD__);
393
    }
394
395
    /**
396
     * {@inheritdoc}
397
     */
398
    protected function reconnectIfMissingConnection()
399
    {
400
        if (is_null($this->connection)) {
401
            $this->reconnect();
402
        }
403
    }
404
405
    /**
406
     * {@inheritdoc}
407
     */
408
    public function disconnect()
409
    {
410
        $this->connection = null;
411
    }
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
    public function upsert($query, $bindings = [])
437
    {
438
        return $this->affectingStatement($query, $bindings);
439
    }
440
441
    /**
442
     * Get a new query builder instance.
443
     *
444
     * @return QueryBuilder
445
     */
446
    public function query()
447
    {
448
        return new QueryBuilder(
449
            $this, $this->getQueryGrammar(), $this->getPostProcessor()
450
        );
451
    }
452
453
    /**
454
     * @param string|null $bucket
455
     *
456
     * @return View
457
     */
458
    public function view($bucket = null)
459
    {
460
        $bucket = is_null($bucket) ? $this->bucket : $bucket;
461
462
        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
    public function update($query, $bindings = [])
474
    {
475
        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
    public function delete($query, $bindings = [])
487
    {
488
        return $this->positionalStatement($query, $bindings);
489
    }
490
491
    /**
492
     * @return \string[]
493 1
     */
494
    public function metrics()
495 1
    {
496
        return $this->metrics;
497 1
    }
498
499
    /**
500
     * @param \CouchbaseN1qlQuery $queryObject
501
     */
502
    protected function firePreparedQuery(\CouchbaseN1qlQuery $queryObject)
503
    {
504
        if (isset($this->events)) {
505
            $this->events->fire(new QueryPrepared($queryObject));
506
        }
507
    }
508
509
    /**
510
     * @param mixed $returning
511
     */
512
    protected function fireReturning($returning)
513
    {
514
        if (isset($this->events)) {
515
            $this->events->fire(new ResultReturning($returning));
516
        }
517
    }
518
}
519