GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( cc509a...8bfdba )
by James Ekow Abaka
13s queued 11s
created

RecordWrapper::toArray()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4.1755

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 7
cts 9
cp 0.7778
rs 9.7333
c 0
b 0
f 0
cc 4
nc 3
nop 2
crap 4.1755
1
<?php
2
3
/*
4
 * The MIT License
5
 *
6
 * Copyright 2014-2018 James Ekow Abaka Ainooson
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated documentation files (the "Software"), to deal
10
 * in the Software without restriction, including without limitation the rights
11
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
 * copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
 * THE SOFTWARE.
25
 */
26
27
namespace ntentan\nibii;
28
29
use ntentan\nibii\exceptions\NibiiException;
30
use ntentan\utils\Text;
31
32
/**
33
 * An active record wrapper for database records.
34
 */
35
class RecordWrapper implements \ArrayAccess, \Countable, \Iterator
36
{
37
    /**
38
     * An associative array of models to which this model has a one to may relationship.
39
     *
40
     * @var array
41
     */
42
    protected $hasMany = [];
43
44
    /**
45
     * An associative array of models which have a one-to-many relationship with this model.
46
     *
47
     * @var array
48
     */
49
    protected $belongsTo = [];
50
51
    /**
52
     * An associative array of models with which this model has a many to many relationship.
53
     *
54
     * @var array
55
     */
56
    protected $manyHaveMany = [];
57
58
    /**
59
     * The name of the database table.
60
     *
61
     * @var string
62
     */
63
    protected $table;
64
65
    /**
66
     * The name of the schema to which this table belongs.
67
     *
68
     * @var string
69
     */
70
    protected $schema;
71
72
    /**
73
     * Temporary data held in the model object.
74
     *
75
     * @var array
76
     */
77
    protected $modelData = [];
78
79
    /**
80
     * Extra validation rules to use over the model's inherent validation requirements.
81
     * @var array
82
     */
83
    protected $validationRules = [];
84
85
    /**
86
     * A quoted string of the table name used for building queries.
87
     *
88
     * @var string
89
     */
90
    private $quotedTable;
91
92
    /**
93
     * The raw table name without any quotes.
94
     *
95
     * @var string
96
     */
97
    private $unquotedTable;
98
99
    /**
100
     * An array of fields that contain validation errors after an attempted save.
101
     *
102
     * @var array
103
     */
104
    private $invalidFields;
105
106
    /**
107
     * An instance of the operations dispatcher.
108
     *
109
     * @var Operations
110
     */
111
    private $dynamicOperations;
112
113
    /**
114
     * Location of the RecordWrapper's internal iterator.
115
     *
116
     * @var int
117
     */
118
    private $index = 0;
119
120
    /**
121
     * This flag is set whenever data is manually put in the model with the setData method.
122
     *
123
     * @var bool
124
     */
125
    private $dataSet = false;
126
127
    /**
128
     * The name of the class for this model obtained through reflection.
129
     *
130
     * @var string
131
     */
132
    private $className;
133
134
    /**
135
     * An instance of the driver adapter for interacting with the database.
136
     *
137
     * @var DriverAdapter
138
     */
139
    private $adapter;
140
141
    /**
142
     * An instance of the ORMContext through which this model is operating.
143
     *
144
     * @var ORMContext
145
     */
146
    private $context;
147
148
    /**
149
     * Keys for the various fields when model is accessed as an associative array.
150
     *
151
     * @var array
152
     */
153
    private $keys = [];
154
155
    /**
156
     * This flag is set after the model has been properly initialized.
157
     * Useful after model is unserialized or accessed through the static interface.
158
     *
159
     * @var bool
160
     */
161
    private $initialized = false;
162
163
    /**
164
     * Initialize the record wrapper and setup the adapters, drivers, tables and schemas.
165
     * After initialization, this method sets the initialized flag.
166
     *
167
     * @return void
168
     */
169 36
    protected function initialize(): void
170
    {
171 36
        if ($this->initialized) {
172 36
            return;
173
        }
174 36
        $this->context = ORMContext::getInstance();
0 ignored issues
show
Documentation Bug introduced by
It seems like \ntentan\nibii\ORMContext::getInstance() of type object<self> is incompatible with the declared type object<ntentan\nibii\ORMContext> of property $context.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
175 36
        $this->adapter = $this->context->getDriverAdapter();
176 36
        $table = $this->table ?? $this->context->getModelTable($this);
177 36
        $driver = $this->context->getDbContext()->getDriver();
178 36
        $this->adapter->setContext($this->context);
179 36
        $this->className = (new \ReflectionClass($this))->getName();
180 36
        if (is_string($table)) {
181 36
            $this->table = $this->unquotedTable = $table;
182
        } else {
183
            $this->table = $table['table'];
184
            $this->schema = $table['schema'];
185
        }
186 36
        $this->quotedTable = ($this->schema ? "{$driver->quoteIdentifier($this->schema)}." : '').$driver->quoteIdentifier($this->table);
187 36
        $this->unquotedTable = ($this->schema ? "{$this->schema}." : '').$this->table;
188 36
        $this->adapter->setModel($this, $this->quotedTable);
189 36
        $this->initialized = true;
190 36
    }
191
192
    public function __debugInfo()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
193
    {
194
        $data = $this->getData();
195
196
        return $this->hasMultipleItems() ? $data : isset($data[0]) ? $data[0] : [];
197
    }
198
199
    /**
200
     * Return a description of the model wrapped by this wrapper.
201
     *
202
     * @return ModelDescription
203
     */
204 28
    public function getDescription() : ModelDescription
205
    {
206 28
        $this->initialize();
207
208 28
        return $this->context->getCache()->read(
209
            "{$this->className}::desc", function () {
210 28
                return $this->context->getModelDescription($this);
211 28
            }
212
        );
213
    }
214
215
    /**
216
     * Return the number of items stored in the model or matched by the query.
217
     * Depending on the state of the model, the count method will return different values. For models that have data
218
     * values set with calls to setData, this method returns the number of records that were added. On the other hand,
219
     * for models that do not have data set, this method queries the database to find out the number of records that
220
     * are either in the model, or for models that have been filtered, the number of records that match the filter.
221
     *
222
     * @param int|array|QueryParameters $query
223
     *
224
     * @return int
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|type?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
225
     */
226 8
    public function count($query = null)
227
    {
228 8
        if ($this->dataSet) {
229 8
            return count($this->getData());
230
        }
231
232
        return $this->__call('count', [$query]);
233
    }
234
235
    /**
236
     * Retrieve an item stored in the record.
237
     * This method returns items that are directly stored in the model, or lazy loads related items if needed.
238
     * The key could be a field in the model's table or the name of a related model.
239
     *
240
     * @param string $key A key identifying the item to be retrieved.
241
     *
242
     * @return mixed
243
     */
244 12
    private function retrieveItem($key)
245
    {
246 12
        if ($this->hasMultipleItems()) {
247
            throw new NibiiException('Current model object state contains multiple items. Please index with a numeric key to select a specific item first.');
248
        }
249 12
        $relationships = $this->getDescription()->getRelationships();
250 12
        $decamelizedKey = Text::deCamelize($key);
251 12
        if (isset($relationships[$key])) {
252 8
            return $this->fetchRelatedFields($relationships[$key]);
253
        }
254
255 4
        return isset($this->modelData[$decamelizedKey]) ? $this->modelData[$decamelizedKey] : null;
256
    }
257
258
    /**
259
     * Calls dynamic methods.
260
     *
261
     * @param string $name
262
     * @param array  $arguments
263
     *
264
     * @throws exceptions\NibiiException
265
     *
266
     * @return type
267
     */
268 34
    public function __call($name, $arguments)
269
    {
270 34
        $this->initialize();
271 34
        if ($this->dynamicOperations === null) {
272 34
            $this->dynamicOperations = new Operations($this, $this->quotedTable);
273
        }
274
275 34
        return $this->dynamicOperations->perform($name, $arguments);
276
    }
277
278
    /**
279
     * Set a value for a field in the model.
280
     *
281
     * @param string $name
282
     * @param mixed  $value
283
     */
284 8
    public function __set($name, $value)
285
    {
286 8
        $this->dataSet = true;
287 8
        $this->modelData[Text::deCamelize($name)] = $value;
288 8
    }
289
290 12
    public function __get($name)
291
    {
292 12
        return $this->retrieveItem($name);
293
    }
294
295 10
    public function save()
296
    {
297 10
        $return = $this->__call('save', [$this->hasMultipleItems()]);
298 10
        $this->invalidFields = $this->dynamicOperations->getInvalidFields();
299
300 10
        return $return;
301
    }
302
303 26
    private function hasMultipleItems()
304
    {
305 26
        if (count($this->modelData) > 0) {
306 24
            return is_numeric(array_keys($this->modelData)[0]);
307
        } else {
308 2
            return false;
309
        }
310
    }
311
312 18
    public function getData()
313
    {
314 18
        $data = [];
315
316 18
        if (count($this->modelData) == 0) {
317 2
            $data = $this->modelData;
318 16
        } elseif ($this->hasMultipleItems()) {
319 6
            $data = $this->modelData;
320 12
        } elseif (count($this->modelData) > 0) {
321 12
            $data[] = $this->modelData;
322
        }
323
324 18
        return $data;
325
    }
326
327 26
    public function setData($data)
328
    {
329 26
        $this->dataSet = is_array($data) ? true : false;
330 26
        $this->modelData = $data;
331 26
    }
332
333
    public function mergeData($data)
334
    {
335
        foreach ($data as $key => $value) {
336
            $this->modelData[$key] = $value;
337
        }
338
        $this->dataSet = true;
339
    }
340
341 2
    public function offsetExists($offset)
342
    {
343 2
        return isset($this->modelData[$offset]);
344
    }
345
346 2
    public function offsetGet($offset)
347
    {
348 2
        if (is_numeric($offset)) {
349 2
            return $this->wrap($offset);
350
        } else {
351 2
            return $this->retrieveItem($offset);
352
        }
353
    }
354
355 2
    public function offsetSet($offset, $value)
356
    {
357 2
        $this->dataSet = true;
358 2
        $this->modelData[$offset] = $value;
359 2
    }
360
361
    public function offsetUnset($offset)
362
    {
363
        unset($this->modelData[$offset]);
364
    }
365
366 6
    private function wrap($offset)
367
    {
368 6
        $this->initialize();
369 6
        if (isset($this->modelData[$offset])) {
370 6
            $className = $this->className;
371 6
            $newInstance = new $className();
372 6
            $newInstance->initialize();
373 6
            $newInstance->setData($this->modelData[$offset]);
374
375 6
            return $newInstance;
376
        } else {
377
            return;
378
        }
379
    }
380
381 4
    public function getInvalidFields()
382
    {
383 4
        return $this->invalidFields;
384
    }
385
386
    public function getHasMany()
387
    {
388
        return $this->hasMany;
389
    }
390
391
    public function getBelongsTo()
392
    {
393
        return $this->belongsTo;
394
    }
395
396 4
    public function current()
397
    {
398 4
        return $this->wrap($this->keys[$this->index]);
399
    }
400
401
    public function key()
402
    {
403
        return $this->keys[$this->index];
404
    }
405
406 4
    public function next()
407
    {
408 4
        $this->index++;
409 4
    }
410
411 4
    public function rewind()
412
    {
413 4
        $this->keys = array_keys($this->modelData);
414 4
        $this->index = 0;
415 4
    }
416
417 4
    public function valid()
418
    {
419 4
        return isset($this->keys[$this->index]) && isset($this->modelData[$this->keys[$this->index]]);
420
    }
421
422
    /**
423
     * A custom validator for the record wrapper.
424
     *
425
     * @return mixed
426
     */
427 10
    public function onValidate($invalidFields) : array 
0 ignored issues
show
Unused Code introduced by
The parameter $invalidFields is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
428
    {
429 10
        return [];
430
    }
431
432 12
    private function fetchRelatedFields($relationship, $index = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
433
    {
434 12
        if ($index === null) {
435 12
            $data = $this->modelData;
436
        } else {
437
            $data = $this->modelData[$index];
438
        }
439 12
        $model = $relationship->getModelInstance();
440 12
        if (empty($data)) {
441
            return $model;
442
        }
443 12
        $query = $relationship->prepareQuery($data);
444
445 12
        return $query ? $model->fetch($query) : $model;
446
    }
447
448 28
    public function getRelationships()
449
    {
450
        return [
451 28
            'HasMany'      => $this->hasMany,
452 28
            'BelongsTo'    => $this->belongsTo,
453 28
            'ManyHaveMany' => $this->manyHaveMany,
454
        ];
455
    }
456
457
    public function usetField($field)
458
    {
459
        unset($this->modelData[$field]);
460
    }
461
462
    /**
463
     * Callback for when a record is either added or modified.
464
     */
465 10
    public function preSaveCallback()
466
    {
467 10
    }
468
469
    /**
470
     * Callback for when a record has been added or modified.
471
     *
472
     * @param $id
473
     */
474 6
    public function postSaveCallback()
475
    {
476 6
    }
477
478
    /**
479
     * Callback for when a new record is about to be created.
480
     */
481 8
    public function preCreateCallback()
482
    {
483 8
    }
484
485
    /**
486
     * Callback for when a new record has been created.
487
     * This callback can be most useful for obtaining the primary key of a newly created record.
488
     *
489
     * @param $id
490
     */
491 4
    public function postCreateCallback($id)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
492
    {
493 4
    }
494
495
    /**
496
     * Callback for when a record is about to be updated.
497
     */
498 2
    public function preUpdateCallback()
499
    {
500 2
    }
501
502
    /**
503
     * Callback for when a record has been updated.
504
     */
505 2
    public function postUpdateCallback()
506
    {
507 2
    }
508
509 36
    public function getDBStoreInformation()
510
    {
511 36
        $this->initialize();
512
513
        return [
514 36
            'schema'         => $this->schema,
515 36
            'table'          => $this->table,
516 36
            'quoted_table'   => $this->quotedTable,
517 36
            'unquoted_table' => $this->unquotedTable,
518
        ];
519
    }
520
521
    /**
522
     * @return DriverAdapter
523
     */
524 36
    public function getAdapter()
525
    {
526 36
        $this->initialize();
527 36
        return $this->adapter;
528
    }
529
530 4
    private function expandArrayValue($array, $relationships, $depth, $expandableModels = [], $index = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
531
    {
532 4
        if (empty($expandableModels)) {
533 4
            foreach ($relationships as $name => $relationship) {
534 4
                $array[$name] = $this->fetchRelatedFields($relationship, $index)->toArray($depth);
535
            }
536
        } else {
537
            foreach ($expandableModels as $name) {
538
                $array[$name] = $this->fetchRelatedFields($relationships[$name], $index)->toArray($depth, $expandableModels);
539
            }
540
        }
541
542 4
        return $array;
543
    }
544
545 10
    public function getValidationRules() : array
546
    {
547 10
        return $this->validationRules;
548
    }
549
550 24
    public function toArray($depth = 0, $expandableModels = [])
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
551
    {
552 24
        $relationships = $this->getDescription()->getRelationships();
553 24
        $array = $this->modelData;
554 24
        if ($depth > 0) {
555 4
            if ($this->hasMultipleItems()) {
556
                foreach ($array as $i => $value) {
557
                    $array[$i] = $this->expandArrayValue($value, $relationships, $depth - 1, $expandableModels, $i);
558
                }
559
            } else {
560 4
                $array = $this->expandArrayValue($array, $relationships, $depth - 1, $expandableModels);
561
            }
562
        }
563
564 24
        return $array;
565
    }
566
}
567