Completed
Push — master ( 470d43...3f2648 )
by Gabriel
03:36
created

ActiveRecordsTrait::deleteByParams()   A

Complexity

Conditions 6
Paths 12

Size

Total Lines 28
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
cc 6
eloc 15
nc 12
nop 1
dl 0
loc 28
ccs 0
cts 15
cp 0
crap 42
rs 9.2222
c 0
b 0
f 0
1
<?php
2
3
namespace Nip\Records\Traits\ActiveRecord;
4
5
use Nip\Container\Container;
6
use Nip\Database\Connections\Connection;
7
use Nip\Database\Query\AbstractQuery as Query;
8
use Nip\Database\Query\Delete as DeleteQuery;
9
use Nip\Database\Query\Insert as InsertQuery;
10
use Nip\Database\Query\Select as SelectQuery;
11
use Nip\Database\Query\Update as UpdateQuery;
12
use Nip\Database\Result;
13
use Nip\Paginator;
0 ignored issues
show
Bug introduced by
The type Nip\Paginator was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use Nip\Records\AbstractModels\Record;
15
use Nip\Records\Collections\Collection as RecordCollection;
16
use Nip\Records\Traits\Unique\RecordsTrait as UniqueRecordsTrait;
17
use Nip\Records\Traits\HasForeignKey\RecordsTrait as HasForeignKeyTrait;
18
19
/**
20
 * Class ActiveRecordsTrait
21
 * @package Nip\Records\Traits\ActiveRecord
22
 */
23
trait ActiveRecordsTrait
24
{
25
    use UniqueRecordsTrait;
26
    use HasForeignKeyTrait;
27
28
    /**
29
     * @var Connection
30
     */
31
    protected $connection = null;
32
33
    /**
34
     * @var null|string
35
     */
36
    protected $table = null;
37
38
    protected $tableStructure = null;
39
40
    /**
41
     * @var null|string
42
     */
43
    protected $primaryKey = null;
44
    protected $fields = null;
45
46
    /**
47
     * @return SelectQuery
48
     */
49
    public function newSelectQuery()
50
    {
51
        return $this->newQuery('select');
52
    }
53
54
    /**
55
     * Factory
56
     * @param string $type
57
     * @return Query|SelectQuery|InsertQuery|DeleteQuery|UpdateQuery
58
     */
59 5
    public function newQuery($type = 'select')
60
    {
61 5
        $query = $this->getDB()->newQuery($type);
62 5
        $query->cols("`" . $this->getTable() . "`.*");
0 ignored issues
show
Bug introduced by
'`' . $this->getTable() . '`.*' of type string is incompatible with the type array expected by parameter $| of Nip\Database\Query\AbstractQuery::cols(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

62
        $query->cols(/** @scrutinizer ignore-type */ "`" . $this->getTable() . "`.*");
Loading history...
63 5
        $query->from($this->getFullNameTable());
0 ignored issues
show
Bug introduced by
$this->getFullNameTable() of type string is incompatible with the type array expected by parameter $| of Nip\Database\Query\AbstractQuery::from(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

63
        $query->from(/** @scrutinizer ignore-type */ $this->getFullNameTable());
Loading history...
64 5
        $query->table($this->getTable());
0 ignored issues
show
Bug introduced by
$this->getTable() of type string is incompatible with the type array expected by parameter $| of Nip\Database\Query\AbstractQuery::table(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

64
        $query->table(/** @scrutinizer ignore-type */ $this->getTable());
Loading history...
65
66 5
        return $query;
67
    }
68
69
    /**
70
     * @return Connection
71
     */
72 7
    public function getDB()
73
    {
74 7
        if ($this->connection == null) {
75 3
            $this->initDB();
76
        }
77
78 7
        $this->checkDB();
79
80 7
        return $this->connection;
81
    }
82
83 3
    protected function initDB()
84
    {
85 3
        $this->setDB($this->newDbConnection());
86 3
    }
87
88
    /**
89
     * @param Connection $connection
90
     * @return $this
91
     */
92 24
    public function setDB($connection)
93
    {
94 24
        $this->connection = $connection;
95
96 24
        return $this;
97
    }
98
99
    /**
100
     * @return Connection
101
     */
102 3
    protected function newDbConnection()
103
    {
104 3
        if (function_exists('app')) {
105
            return app('db.connection');
106
        }
107 3
        return Container::getInstance()->get('db.connection');
108
    }
109
110 7
    public function checkDB()
111
    {
112 7
        if (!$this->hasDB()) {
113
            trigger_error("Database connection missing for [" . get_class($this) . "]", E_USER_ERROR);
114
        }
115 7
    }
116
117
    /**
118
     * @return bool
119
     */
120 7
    public function hasDB()
121
    {
122 7
        return $this->connection instanceof Connection;
123
    }
124
125
    /**
126
     * @return string
127
     */
128 10
    public function getTable()
129
    {
130 10
        if ($this->table === null) {
131 7
            $this->initTable();
132
        }
133
134 10
        return $this->table;
135
    }
136
137
    /**
138
     * @param string $table
139
     */
140 29
    public function setTable($table)
141
    {
142 29
        $this->table = $table;
143 29
    }
144
145 7
    protected function initTable()
146
    {
147 7
        $this->setTable($this->generateTable());
148 7
    }
149
150
    /**
151
     * @return string
152
     */
153 5
    protected function generateTable()
154
    {
155 5
        return str_replace('-', '_', $this->getController());
0 ignored issues
show
Bug introduced by
It seems like getController() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

155
        return str_replace('-', '_', $this->/** @scrutinizer ignore-call */ getController());
Loading history...
156
    }
157
158
    /**
159
     * @return string
160
     */
161 7
    public function getFullNameTable()
162
    {
163 7
        $database = $this->getDB()->getDatabase();
164
165 7
        return $database ? $database . '.' . $this->getTable() : $this->getTable();
166
    }
167
168
    /**
169
     * @return null
170
     */
171
    public function getFields()
172
    {
173
        if ($this->fields === null) {
174
            $this->initFields();
175
        }
176
177
        return $this->fields;
178
    }
179
180
    public function initFields()
181
    {
182
        $structure = $this->getTableStructure();
183
        $this->fields = array_keys($structure['fields']);
184
    }
185
186
    /**
187
     * @return mixed
188
     */
189 2
    protected function getTableStructure()
190
    {
191 2
        if ($this->tableStructure == null) {
192
            $this->initTableStructure();
193
        }
194
195 2
        return $this->tableStructure;
196
    }
197
198
    /**
199
     * @param null $tableStructure
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $tableStructure is correct as it would always require null to be passed?
Loading history...
200
     */
201 3
    public function setTableStructure($tableStructure)
202
    {
203 3
        $this->tableStructure = $tableStructure;
204 3
    }
205
206
    protected function initTableStructure()
207
    {
208
        $this->setTableStructure($this->getDB()->getMetadata()->describeTable($this->getTable()));
209
    }
210
211
    /**
212
     * @return InsertQuery
213
     */
214
    public function newInsertQuery()
215
    {
216
        return $this->newQuery('insert');
217
    }
218
219
    /**
220
     * Updates a Record's database entry
221
     * @param Record $model
222
     * @return bool|Result
223
     */
224
    public function update(Record $model)
225
    {
226
        $query = $this->updateQuery($model);
227
228
        if ($query) {
229
            return $query->execute();
230
        }
231
232
        return false;
233
    }
234
235
    /**
236
     * @param Record $model
237
     * @return bool|UpdateQuery
238
     */
239
    public function updateQuery(Record $model)
240
    {
241
        $pk = $this->getPrimaryKey();
242
        if (!is_array($pk)) {
0 ignored issues
show
introduced by
The condition is_array($pk) is always false.
Loading history...
243
            $pk = [$pk];
244
        }
245
246
        $data = $this->getQueryModelData($model);
0 ignored issues
show
Bug introduced by
It seems like getQueryModelData() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

246
        /** @scrutinizer ignore-call */ 
247
        $data = $this->getQueryModelData($model);
Loading history...
247
248
        if ($data) {
249
            $query = $this->newUpdateQuery();
250
            $query->data($data);
251
252
            foreach ($pk as $key) {
253
                $query->where("$key = ?", $model->{$key});
254
            }
255
256
            return $query;
257
        }
258
259
        return false;
260
    }
261
262
    /**
263
     * @return string
264
     */
265 15
    public function getPrimaryKey()
266
    {
267 15
        if ($this->primaryKey === null) {
268 3
            $this->initPrimaryKey();
269
        }
270
271 15
        return $this->primaryKey;
272
    }
273
274
    /**
275
     * @param null|string $primaryKey
276
     */
277 15
    public function setPrimaryKey($primaryKey)
278
    {
279 15
        $this->primaryKey = $primaryKey;
280 15
    }
281
282 3
    protected function initPrimaryKey()
283
    {
284 3
        $this->setPrimaryKey($this->generatePrimaryKey());
285 3
    }
286
287
    /**
288
     * @return string
289
     */
290
    public function generatePrimaryKey()
291
    {
292
        $structure = $this->getTableStructure();
293
        $primaryKey = false;
294
        if (is_array($structure) && isset($structure['indexes']['PRIMARY']['fields'])) {
295
            $primaryKey = $structure['indexes']['PRIMARY']['fields'];
296
            if (count($primaryKey) == 1) {
297
                $primaryKey = reset($primaryKey);
298
            }
299
        }
300
301
        return $primaryKey;
302
    }
303
304
    /**
305
     * @return UpdateQuery
306
     */
307
    public function newUpdateQuery()
308
    {
309
        return $this->newQuery('update');
310
    }
311
312
    /**
313
     * Saves a Record's database entry
314
     * @param Record $model
315
     * @return mixed
316
     */
317
    public function save(Record $model)
318
    {
319
        $pk = $this->getPrimaryKey();
320
321
        if (isset($model->{$pk})) {
322
            $model->update();
323
324
            return $model->{$pk};
325
        } else {
326
            /** @var Record $previous */
327
            $previous = $model->exists();
328
329
            if ($previous) {
0 ignored issues
show
introduced by
$previous is of type Nip\Records\AbstractModels\Record, thus it always evaluated to true.
Loading history...
330
                $data = $model->toArray();
331
332
                if ($data) {
333
                    $previous->writeData($model->toArray());
334
                }
335
                $previous->update();
336
337
                $model->writeData($previous->toArray());
338
339
                return $model->getPrimaryKey();
340
            }
341
        }
342
343
        $model->insert();
344
345
        return $model->getPrimaryKey();
346
    }
347
348
    /**
349
     * Delete a Record's database entry
350
     *
351
     * @param Record $input
352
     */
353
    public function delete($input)
354
    {
355
        $pk = $this->getPrimaryKey();
356
357
        if ($input instanceof $this->model) {
358
            $primary = $input->getPrimaryKey();
359
        } else {
360
            $primary = $input;
361
        }
362
363
        $query = $this->newDeleteQuery();
364
        $query->where("$pk = ?", $primary);
365
        $query->limit(1);
366
367
        $this->getDB()->execute($query);
368
    }
369
370
    /**
371
     * @return DeleteQuery
372
     */
373
    public function newDeleteQuery()
374
    {
375
        return $this->newQuery('delete');
376
    }
377
378
    /**
379
     * Delete a Record's database entry
380
     * @param array $params
381
     * @return $this
382
     */
383
    public function deleteByParams($params = [])
384
    {
385
        extract($params);
386
387
        $query = $this->newDeleteQuery();
388
389
        if (isset($where)) {
390
            if (is_array($where)) {
391
                foreach ($where as $condition) {
392
                    $condition = (array)$condition;
393
                    $query->where($condition[0], $condition[1]);
394
                }
395
            } else {
396
                call_user_func_array([$query, 'where'], $where);
397
            }
398
        }
399
400
        if (isset($order)) {
401
            call_user_func_array([$query, 'order'], $order);
402
        }
403
404
        if (isset($limit)) {
405
            call_user_func_array([$query, 'limit'], $limit);
406
        }
407
408
        $this->getDB()->execute($query);
409
410
        return $this;
411
    }
412
413
    /**
414
     * Returns paginated results
415
     * @param Paginator $paginator
416
     * @param array $params
417
     * @return mixed
418
     */
419
    public function paginate(Paginator $paginator, $params = [])
420
    {
421
        $query = $this->paramsToQuery($params);
422
423
        $countQuery = $this->getDB()->newSelect();
424
        $countQuery->count(['*', 'count']);
0 ignored issues
show
Bug introduced by
array('*', 'count') of type array<integer,string> is incompatible with the type string expected by parameter $col of Nip\Database\Query\AbstractQuery::count(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

424
        $countQuery->count(/** @scrutinizer ignore-type */ ['*', 'count']);
Loading history...
425
        $countQuery->from([$query, 'tbl']);
426
        $results = $countQuery->execute()->fetchResults();
427
        $count = $results[0]['count'];
428
429
        $paginator->setCount($count);
430
431
        $params['limit'] = $paginator->getLimits();
432
433
        return $this->findByParams($params);
0 ignored issues
show
Bug introduced by
It seems like findByParams() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

433
        return $this->/** @scrutinizer ignore-call */ findByParams($params);
Loading history...
434
    }
435
436
    /**
437
     * @param array $params
438
     * @return SelectQuery
439
     */
440 3
    public function paramsToQuery($params = [])
441
    {
442 3
        $this->injectParams($params);
443
444 3
        $query = $this->newQuery('select');
445 3
        $query->addParams($params);
446
447 3
        return $query;
448
    }
449
450
    /**
451
     * @param array $params
452
     */
453 3
    protected function injectParams(&$params = [])
454
    {
455 3
    }
456
457
    /**
458
     * Checks the registry before fetching from the database
459
     * @param mixed $primary
460
     * @return Record
461
     */
462
    public function findOne($primary)
463
    {
464
        $item = $this->getRegistry()->get($primary);
0 ignored issues
show
Bug introduced by
It seems like getRegistry() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

464
        $item = $this->/** @scrutinizer ignore-call */ getRegistry()->get($primary);
Loading history...
465
        if (!$item) {
466
            $all = $this->getRegistry()->get("all");
467
            if ($all) {
468
                $item = $all[$primary];
469
            }
470
            if (!$item) {
471
                $params['where'][] = ["`{$this->getTable()}`.`{$this->getPrimaryKey()}` = ?", $primary];
0 ignored issues
show
Comprehensibility Best Practice introduced by
$params was never initialized. Although not strictly required by PHP, it is generally a good practice to add $params = array(); before regardless.
Loading history...
472
                $item = $this->findOneByParams($params);
0 ignored issues
show
Bug introduced by
The method findOneByParams() does not exist on Nip\Records\Traits\ActiveRecord\ActiveRecordsTrait. Did you maybe mean findOne()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

472
                /** @scrutinizer ignore-call */ 
473
                $item = $this->findOneByParams($params);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
473
                if ($item) {
474
                    $this->getRegistry()->set($primary, $item);
475
                }
476
477
                return $item;
478
            }
479
        }
480
481
        return $item;
482
    }
483
484
    /**
485
     * @param Query $query
486
     * @param array $params
487
     * @return bool
488
     */
489
    public function findOneByQuery($query, $params = [])
490
    {
491
        $query->limit(1);
492
        $return = $this->findByQuery($query, $params);
493
        if (count($return) > 0) {
494
            return $return->rewind();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $return->rewind() targeting Nip\Collections\AbstractCollection::rewind() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug Best Practice introduced by
The expression return $return->rewind() returns the type void which is incompatible with the documented return type boolean.
Loading history...
495
        }
496
497
        return null;
498
    }
499
500
    /**
501
     * @param Query $query
502
     * @param array $params
503
     * @return RecordCollection
504
     */
505
    public function findByQuery($query, $params = [])
506
    {
507
        $return = $this->newCollection();
0 ignored issues
show
Bug introduced by
It seems like newCollection() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

507
        /** @scrutinizer ignore-call */ 
508
        $return = $this->newCollection();
Loading history...
508
509
        $results = $this->getDB()->execute($query);
510
        if ($results->numRows() > 0) {
511
            $pk = $this->getPrimaryKey();
512
            /** @noinspection PhpAssignmentInConditionInspection */
513
            while ($row = $results->fetchResult()) {
514
                $item = $this->getNew($row);
0 ignored issues
show
Bug introduced by
It seems like getNew() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

514
                /** @scrutinizer ignore-call */ 
515
                $item = $this->getNew($row);
Loading history...
515
                if (is_string($pk)) {
516
                    $this->getRegistry()->set($item->getPrimaryKey(), $item);
517
                }
518
                if (isset($params['indexKey']) && !empty($params['indexKey'])) {
519
                    $return->add($item, $params['indexKey']);
520
                } else {
521
                    $return->add($item);
522
                }
523
            }
524
        }
525
526
        return $return;
527
    }
528
529
    /**
530
     * @param bool|array $where
531
     * @return int
532
     */
533
    public function count($where = false)
534
    {
535
        return $this->countByParams(["where" => $where]);
536
    }
537
538
    /**
539
     * Counts all the Record entries in the database
540
     * @param array $params
541
     * @return int
542
     */
543
    public function countByParams($params = [])
544
    {
545
        $this->injectParams($params);
546
        $query = $this->newQuery('select');
547
        $query->addParams($params);
548
549
        return $this->countByQuery($query);
550
    }
551
552
    /**
553
     * Counts all the Record entries in the database
554
     * @param Query $query
555
     * @return int
556
     */
557
    public function countByQuery($query)
558
    {
559
        $queryCount = clone $query;
560
        $queryCount->setCols('count(*) as count');
0 ignored issues
show
Bug introduced by
'count(*) as count' of type string is incompatible with the type array expected by parameter $| of Nip\Database\Query\AbstractQuery::setCols(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

560
        $queryCount->setCols(/** @scrutinizer ignore-type */ 'count(*) as count');
Loading history...
561
        $result = $this->getDB()->execute($queryCount);
562
563
        if ($result->numRows()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result->numRows() of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
564
            $row = $result->fetchResult();
565
566
            return (int)$row['count'];
567
        }
568
569
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
570
    }
571
572
    /**
573
     * @param $data
574
     * @return mixed
575
     */
576
    public function cleanData($data)
577
    {
578
        return $this->getDB()->getAdapter()->cleanData($data);
579
    }
580
581
    /**
582
     * @param string $name
583
     * @param $arguments
584
     * @return RecordCollection|false
585
     */
586 1
    protected function isCallDatabaseOperation($name, $arguments)
587
    {
588 1
        $operations = ["find", "delete", "count"];
589 1
        foreach ($operations as $operation) {
590 1
            if (strpos($name, $operation . "By") !== false || strpos($name, $operation . "OneBy") !== false) {
591
                $params = [];
592
                if (count($arguments) > 1) {
593
                    $params = end($arguments);
594
                }
595
596
                $match = str_replace([$operation . "By", $operation . "OneBy"], "", $name);
597
                $field = inflector()->underscore($match);
598
599
                if ($field == $this->getPrimaryKey()) {
600
                    return $this->findByPrimary($arguments[0]);
0 ignored issues
show
Bug introduced by
It seems like findByPrimary() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

600
                    return $this->/** @scrutinizer ignore-call */ findByPrimary($arguments[0]);
Loading history...
601
                }
602
603
                $params['where'][] = ["$field " . (is_array($arguments[0]) ? "IN" : "=") . " ?", $arguments[0]];
604
605
                $operation = str_replace($match, "", $name) . "Params";
606
607
                $results = $this->$operation($params);
608
                // RETURN NULL TO DISTINCT FROM FALSE THAT MEANS NOT A DATABASE OPERATION
609 1
                return ($results) ? $results : null;
610
            }
611
        }
612
613 1
        return false;
614
    }
615
}
616