Completed
Push — master ( 19d4fc...09bb07 )
by Gabriel
03:42
created

ActiveRecordsTrait::setPrimaryKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Nip\Records\Traits\ActiveRecord;
4
5
use Nip\Database\Query\AbstractQuery as Query;
6
use Nip\Database\Query\Delete as DeleteQuery;
7
use Nip\Database\Query\Insert as InsertQuery;
8
use Nip\Database\Query\Select as SelectQuery;
9
use Nip\Database\Query\Update as UpdateQuery;
10
use Nip\Database\Result;
11
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...
12
use Nip\Records\AbstractModels\Record;
13
use Nip\Records\Collections\Collection as RecordCollection;
14
use Nip\Records\Traits\HasDatabase\HasDatabaseRecordsTrait;
15
use Nip\Records\Traits\TableStructure\TableStructureRecordsTrait;
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
    use TableStructureRecordsTrait;
28
    use HasDatabaseRecordsTrait;
29
30
    /**
31
     * @var null|string
32
     */
33
    protected $table = null;
34
35
    /**
36
     * @var null|string
37
     */
38
    protected $primaryKey = null;
39
40
    /**
41
     * @return SelectQuery
42
     */
43
    public function newSelectQuery()
44
    {
45
        return $this->newQuery('select');
46
    }
47
48
    /**
49
     * Factory
50
     * @param string $type
51
     * @return Query|SelectQuery|InsertQuery|DeleteQuery|UpdateQuery
52
     */
53 5
    public function newQuery($type = 'select')
54
    {
55 5
        $query = $this->getDB()->newQuery($type);
56 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

56
        $query->cols(/** @scrutinizer ignore-type */ "`" . $this->getTable() . "`.*");
Loading history...
57 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

57
        $query->from(/** @scrutinizer ignore-type */ $this->getFullNameTable());
Loading history...
58 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

58
        $query->table(/** @scrutinizer ignore-type */ $this->getTable());
Loading history...
59
60 5
        return $query;
61
    }
62
63
64
    /**
65
     * @return string
66
     */
67 10
    public function getTable()
68
    {
69 10
        if ($this->table === null) {
70 7
            $this->initTable();
71
        }
72
73 10
        return $this->table;
74
    }
75
76
    /**
77
     * @param string $table
78
     */
79 29
    public function setTable($table)
80
    {
81 29
        $this->table = $table;
82 29
    }
83
84 7
    protected function initTable()
85
    {
86 7
        $this->setTable($this->generateTable());
87 7
    }
88
89
    /**
90
     * @return string
91
     */
92 5
    protected function generateTable()
93
    {
94 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

94
        return str_replace('-', '_', $this->/** @scrutinizer ignore-call */ getController());
Loading history...
95
    }
96
97
    /**
98
     * @return string
99
     */
100 7
    public function getFullNameTable()
101
    {
102 7
        $database = $this->getDB()->getDatabase();
103
104 7
        return $database ? $database . '.' . $this->getTable() : $this->getTable();
105
    }
106
107
    /**
108
     * @return InsertQuery
109
     */
110
    public function newInsertQuery()
111
    {
112
        return $this->newQuery('insert');
113
    }
114
115
    /**
116
     * Updates a Record's database entry
117
     * @param Record $model
118
     * @return bool|Result
119
     */
120
    public function update(Record $model)
121
    {
122
        $query = $this->updateQuery($model);
123
124
        if ($query) {
125
            return $query->execute();
126
        }
127
128
        return false;
129
    }
130
131
    /**
132
     * @param Record $model
133
     * @return bool|UpdateQuery
134
     */
135
    public function updateQuery(Record $model)
136
    {
137
        $pk = $this->getPrimaryKey();
138
        if (!is_array($pk)) {
0 ignored issues
show
introduced by
The condition is_array($pk) is always false.
Loading history...
139
            $pk = [$pk];
140
        }
141
142
        $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

142
        /** @scrutinizer ignore-call */ 
143
        $data = $this->getQueryModelData($model);
Loading history...
143
144
        if ($data) {
145
            $query = $this->newUpdateQuery();
146
            $query->data($data);
147
148
            foreach ($pk as $key) {
149
                $query->where("$key = ?", $model->{$key});
150
            }
151
152
            return $query;
153
        }
154
155
        return false;
156
    }
157
158
    /**
159
     * @return string
160
     */
161 15
    public function getPrimaryKey()
162
    {
163 15
        if ($this->primaryKey === null) {
164 3
            $this->initPrimaryKey();
165
        }
166
167 15
        return $this->primaryKey;
168
    }
169
170
    /**
171
     * @param null|string $primaryKey
172
     */
173 15
    public function setPrimaryKey($primaryKey)
174
    {
175 15
        $this->primaryKey = $primaryKey;
176 15
    }
177
178 3
    protected function initPrimaryKey()
179
    {
180 3
        $this->setPrimaryKey($this->generatePrimaryKey());
181 3
    }
182
183
    /**
184
     * @return string
185
     */
186
    public function generatePrimaryKey()
187
    {
188
        $structure = $this->getTableStructure();
189
        $primaryKey = false;
190
        if (is_array($structure) && isset($structure['indexes']['PRIMARY']['fields'])) {
191
            $primaryKey = $structure['indexes']['PRIMARY']['fields'];
192
            if (count($primaryKey) == 1) {
193
                $primaryKey = reset($primaryKey);
194
            }
195
        }
196
197
        return $primaryKey;
198
    }
199
200
    /**
201
     * @return UpdateQuery
202
     */
203
    public function newUpdateQuery()
204
    {
205
        return $this->newQuery('update');
206
    }
207
208
    /**
209
     * Saves a Record's database entry
210
     * @param Record $model
211
     * @return mixed
212
     */
213
    public function save(Record $model)
214
    {
215
        $pk = $this->getPrimaryKey();
216
217
        if (isset($model->{$pk})) {
218
            $model->update();
219
220
            return $model->{$pk};
221
        } else {
222
            /** @var Record $previous */
223
            $previous = $model->exists();
224
225
            if ($previous) {
0 ignored issues
show
introduced by
$previous is of type Nip\Records\AbstractModels\Record, thus it always evaluated to true.
Loading history...
226
                $data = $model->toArray();
227
228
                if ($data) {
229
                    $previous->writeData($model->toArray());
230
                }
231
                $previous->update();
232
233
                $model->writeData($previous->toArray());
234
235
                return $model->getPrimaryKey();
236
            }
237
        }
238
239
        $model->insert();
240
241
        return $model->getPrimaryKey();
242
    }
243
244
    /**
245
     * Delete a Record's database entry
246
     *
247
     * @param Record $input
248
     */
249
    public function delete($input)
250
    {
251
        $pk = $this->getPrimaryKey();
252
253
        if ($input instanceof $this->model) {
254
            $primary = $input->getPrimaryKey();
255
        } else {
256
            $primary = $input;
257
        }
258
259
        $query = $this->newDeleteQuery();
260
        $query->where("$pk = ?", $primary);
261
        $query->limit(1);
262
263
        $this->getDB()->execute($query);
264
    }
265
266
    /**
267
     * @return DeleteQuery
268
     */
269
    public function newDeleteQuery()
270
    {
271
        return $this->newQuery('delete');
272
    }
273
274
    /**
275
     * Delete a Record's database entry
276
     * @param array $params
277
     * @return $this
278
     */
279
    public function deleteByParams($params = [])
280
    {
281
        extract($params);
282
283
        $query = $this->newDeleteQuery();
284
285
        if (isset($where)) {
286
            if (is_array($where)) {
287
                foreach ($where as $condition) {
288
                    $condition = (array)$condition;
289
                    $query->where($condition[0], $condition[1]);
290
                }
291
            } else {
292
                call_user_func_array([$query, 'where'], $where);
293
            }
294
        }
295
296
        if (isset($order)) {
297
            call_user_func_array([$query, 'order'], $order);
298
        }
299
300
        if (isset($limit)) {
301
            call_user_func_array([$query, 'limit'], $limit);
302
        }
303
304
        $this->getDB()->execute($query);
305
306
        return $this;
307
    }
308
309
    /**
310
     * Returns paginated results
311
     * @param Paginator $paginator
312
     * @param array $params
313
     * @return mixed
314
     */
315
    public function paginate(Paginator $paginator, $params = [])
316
    {
317
        $query = $this->paramsToQuery($params);
318
319
        $countQuery = $this->getDB()->newSelect();
320
        $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

320
        $countQuery->count(/** @scrutinizer ignore-type */ ['*', 'count']);
Loading history...
321
        $countQuery->from([$query, 'tbl']);
322
        $results = $countQuery->execute()->fetchResults();
323
        $count = $results[0]['count'];
324
325
        $paginator->setCount($count);
326
327
        $params['limit'] = $paginator->getLimits();
328
329
        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

329
        return $this->/** @scrutinizer ignore-call */ findByParams($params);
Loading history...
330
    }
331
332
    /**
333
     * @param array $params
334
     * @return SelectQuery
335
     */
336 3
    public function paramsToQuery($params = [])
337
    {
338 3
        $this->injectParams($params);
339
340 3
        $query = $this->newQuery('select');
341 3
        $query->addParams($params);
342
343 3
        return $query;
344
    }
345
346
    /**
347
     * @param array $params
348
     */
349 3
    protected function injectParams(&$params = [])
350
    {
351 3
    }
352
353
    /**
354
     * Checks the registry before fetching from the database
355
     * @param mixed $primary
356
     * @return Record
357
     */
358
    public function findOne($primary)
359
    {
360
        $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

360
        $item = $this->/** @scrutinizer ignore-call */ getRegistry()->get($primary);
Loading history...
361
        if (!$item) {
362
            $all = $this->getRegistry()->get("all");
363
            if ($all) {
364
                $item = $all[$primary];
365
            }
366
            if (!$item) {
367
                $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...
368
                $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

368
                /** @scrutinizer ignore-call */ 
369
                $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...
369
                if ($item) {
370
                    $this->getRegistry()->set($primary, $item);
371
                }
372
373
                return $item;
374
            }
375
        }
376
377
        return $item;
378
    }
379
380
    /**
381
     * @param Query $query
382
     * @param array $params
383
     * @return bool
384
     */
385
    public function findOneByQuery($query, $params = [])
386
    {
387
        $query->limit(1);
388
        $return = $this->findByQuery($query, $params);
389
        if (count($return) > 0) {
390
            return $return->rewind();
0 ignored issues
show
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...
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...
391
        }
392
393
        return null;
394
    }
395
396
    /**
397
     * @param Query $query
398
     * @param array $params
399
     * @return RecordCollection
400
     */
401
    public function findByQuery($query, $params = [])
402
    {
403
        $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

403
        /** @scrutinizer ignore-call */ 
404
        $return = $this->newCollection();
Loading history...
404
405
        $results = $this->getDB()->execute($query);
406
        if ($results->numRows() > 0) {
407
            $pk = $this->getPrimaryKey();
408
            /** @noinspection PhpAssignmentInConditionInspection */
409
            while ($row = $results->fetchResult()) {
410
                $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

410
                /** @scrutinizer ignore-call */ 
411
                $item = $this->getNew($row);
Loading history...
411
                if (is_string($pk)) {
412
                    $this->getRegistry()->set($item->getPrimaryKey(), $item);
413
                }
414
                if (isset($params['indexKey']) && !empty($params['indexKey'])) {
415
                    $return->add($item, $params['indexKey']);
416
                } else {
417
                    $return->add($item);
418
                }
419
            }
420
        }
421
422
        return $return;
423
    }
424
425
    /**
426
     * @param bool|array $where
427
     * @return int
428
     */
429
    public function count($where = false)
430
    {
431
        return $this->countByParams(["where" => $where]);
432
    }
433
434
    /**
435
     * Counts all the Record entries in the database
436
     * @param array $params
437
     * @return int
438
     */
439
    public function countByParams($params = [])
440
    {
441
        $this->injectParams($params);
442
        $query = $this->newQuery('select');
443
        $query->addParams($params);
444
445
        return $this->countByQuery($query);
446
    }
447
448
    /**
449
     * Counts all the Record entries in the database
450
     * @param Query $query
451
     * @return int
452
     */
453
    public function countByQuery($query)
454
    {
455
        $queryCount = clone $query;
456
        $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

456
        $queryCount->setCols(/** @scrutinizer ignore-type */ 'count(*) as count');
Loading history...
457
        $result = $this->getDB()->execute($queryCount);
458
459
        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...
460
            $row = $result->fetchResult();
461
462
            return (int)$row['count'];
463
        }
464
465
        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...
466
    }
467
468
    /**
469
     * @param $data
470
     * @return mixed
471
     */
472
    public function cleanData($data)
473
    {
474
        return $this->getDB()->getAdapter()->cleanData($data);
475
    }
476
477
    /**
478
     * @param string $name
479
     * @param $arguments
480
     * @return RecordCollection|false
481
     */
482 1
    protected function isCallDatabaseOperation($name, $arguments)
483
    {
484 1
        $operations = ["find", "delete", "count"];
485 1
        foreach ($operations as $operation) {
486 1
            if (strpos($name, $operation . "By") !== false || strpos($name, $operation . "OneBy") !== false) {
487
                $params = [];
488
                if (count($arguments) > 1) {
489
                    $params = end($arguments);
490
                }
491
492
                $match = str_replace([$operation . "By", $operation . "OneBy"], "", $name);
493
                $field = inflector()->underscore($match);
494
495
                if ($field == $this->getPrimaryKey()) {
496
                    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

496
                    return $this->/** @scrutinizer ignore-call */ findByPrimary($arguments[0]);
Loading history...
497
                }
498
499
                $params['where'][] = ["$field " . (is_array($arguments[0]) ? "IN" : "=") . " ?", $arguments[0]];
500
501
                $operation = str_replace($match, "", $name) . "Params";
502
503
                $results = $this->$operation($params);
504
                // RETURN NULL TO DISTINCT FROM FALSE THAT MEANS NOT A DATABASE OPERATION
505 1
                return ($results) ? $results : null;
506
            }
507
        }
508
509 1
        return false;
510
    }
511
}
512