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 ( 08adc9...cc509a )
by James Ekow Abaka
18s queued 10s
created

RecordWrapper::getData()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 9
cts 9
cp 1
rs 9.7998
c 0
b 0
f 0
cc 4
nc 4
nop 0
crap 4
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\utils\Text;
30
31
/**
32
 * An active record wrapper for database records.
33
 */
34
class RecordWrapper implements \ArrayAccess, \Countable, \Iterator
35
{
36
    /**
37
     * An associative array of models to which this model has a one to may relationship.
38
     *
39
     * @var array
40
     */
41
    protected $hasMany = [];
42
43
    /**
44
     * An associative array of models which have a one-to-many relationship with this model.
45
     *
46
     * @var array
47
     */
48
    protected $belongsTo = [];
49
50
    /**
51
     * An associative array of models with which this model has a many to many relationship.
52
     *
53
     * @var array
54
     */
55
    protected $manyHaveMany = [];
56
57
    /**
58
     * The name of the database table.
59
     *
60
     * @var string
61
     */
62
    protected $table;
63
64
    /**
65
     * The name of the schema to which this table belongs.
66
     *
67
     * @var string
68
     */
69
    protected $schema;
70
71
    /**
72
     * Temporary data held in the model object.
73
     *
74
     * @var array
75
     */
76
    protected $modelData = [];
77
78
    /**
79
     * A quoted string of the table name used for building queries.
80
     *
81
     * @var string
82
     */
83
    private $quotedTable;
84
85
    /**
86
     * The raw table name without any quotes.
87
     *
88
     * @var string
89
     */
90
    private $unquotedTable;
91
92
    /**
93
     * An array of fields that contain validation errors after an attempted save.
94
     *
95
     * @var array
96
     */
97
    private $invalidFields;
98
99
    /**
100
     * An instance of the operations dispatcher.
101
     *
102
     * @var Operations
103
     */
104
    private $dynamicOperations;
105
106
    /**
107
     * Location of the RecordWrapper's internal iterator.
108
     *
109
     * @var int
110
     */
111
    private $index = 0;
112
113
    /**
114
     * This flag is set whenever data is manually put in the model with the setData method.
115
     *
116
     * @var bool
117
     */
118
    private $dataSet = false;
119
120
    /**
121
     * The name of the class for this model obtained through reflection.
122
     *
123
     * @var string
124
     */
125
    private $className;
126
127
    /**
128
     * An instance of the driver adapter for interacting with the database.
129
     *
130
     * @var DriverAdapter
131
     */
132
    private $adapter;
133
134
    /**
135
     * An instance of the ORMContext through which this model is operating.
136
     *
137
     * @var ORMContext
138
     */
139
    private $context;
140
141
    /**
142
     * Keys for the various fields when model is accessed as an associative array.
143
     *
144
     * @var array
145
     */
146
    private $keys = [];
147
148
    /**
149
     * This flag is set after the model has been properly initialized.
150
     * Useful after model is unserialized or accessed through the static interface.
151
     *
152
     * @var bool
153
     */
154
    private $initialized = false;
155
156
    /**
157
     * Initialize the record wrapper and setup the adapters, drivers, tables and schemas.
158
     * After initialization, this method sets the initialized flag.
159
     *
160
     * @return void
161
     */
162 36
    protected function initialize(): void
163
    {
164 36
        if ($this->initialized) {
165 36
            return;
166
        }
167 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...
168 36
        $this->adapter = $this->context->getDriverAdapter();
169 36
        $table = $this->table ?? $this->context->getModelTable($this);
170 36
        $driver = $this->context->getDbContext()->getDriver();
171 36
        $this->adapter->setContext($this->context);
172 36
        $this->className = (new \ReflectionClass($this))->getName();
173 36
        if (is_string($table)) {
174 36
            $this->table = $this->unquotedTable = $table;
175
        } else {
176
            $this->table = $table['table'];
177
            $this->schema = $table['schema'];
178
        }
179 36
        $this->quotedTable = ($this->schema ? "{$driver->quoteIdentifier($this->schema)}." : '').$driver->quoteIdentifier($this->table);
180 36
        $this->unquotedTable = ($this->schema ? "{$this->schema}." : '').$this->table;
181 36
        $this->adapter->setModel($this, $this->quotedTable);
182 36
        $this->initialized = true;
183 36
    }
184
185
    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...
186
    {
187
        $data = $this->getData();
188
189
        return $this->hasMultipleItems() ? $data : isset($data[0]) ? $data[0] : [];
190
    }
191
192
    /**
193
     * Return a description of the model wrapped by this wrapper.
194
     *
195
     * @return ModelDescription
196
     */
197 28
    public function getDescription() : ModelDescription
198
    {
199 28
        $this->initialize();
200
201 28
        return $this->context->getCache()->read(
202
            "{$this->className}::desc", function () {
203 28
                return $this->context->getModelDescription($this);
204 28
            }
205
        );
206
    }
207
208
    /**
209
     * Return the number of items stored in the model or matched by the query.
210
     * Depending on the state of the model, the count method will return different values. For models that have data
211
     * values set with calls to setData, this method returns the number of records that were added. On the other hand,
212
     * for models that do not have data set, this method queries the database to find out the number of records that
213
     * are either in the model, or for models that have been filtered, the number of records that match the filter.
214
     *
215
     * @param int|array|QueryParameters $query
216
     *
217
     * @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...
218
     */
219 14
    public function count($query = null)
220
    {
221 14
        if ($this->dataSet) {
222 14
            return count($this->getData());
223
        }
224
225
        return $this->__call('count', [$query]);
226
    }
227
228
    /**
229
     * Retrieve an item stored in the record.
230
     * This method returns items that are directly stored in the model or lazy loads related items. The key could be a
231
     * field in the model's table or the name of a related model.
232
     *
233
     * @param string $key A key identifying the item to be retrieved.
234
     *
235
     * @return mixed
236
     */
237 12
    private function retrieveItem($key)
238
    {
239 12
        $relationships = $this->getDescription()->getRelationships();
240 12
        $decamelizedKey = Text::deCamelize($key);
241 12
        if (isset($relationships[$key])) {
242 8
            return $this->fetchRelatedFields($relationships[$key]);
243
        }
244
245 4
        return isset($this->modelData[$decamelizedKey]) ? $this->modelData[$decamelizedKey] : null;
246
    }
247
248
    /**
249
     * Calls dynamic methods.
250
     *
251
     * @param string $name
252
     * @param array  $arguments
253
     *
254
     * @throws exceptions\NibiiException
255
     *
256
     * @return type
257
     */
258 34
    public function __call($name, $arguments)
259
    {
260 34
        $this->initialize();
261 34
        if ($this->dynamicOperations === null) {
262 34
            $this->dynamicOperations = new Operations($this, $this->quotedTable);
263
        }
264
265 34
        return $this->dynamicOperations->perform($name, $arguments);
266
    }
267
268
    /**
269
     * Set a value for a field in the model.
270
     *
271
     * @param string $name
272
     * @param mixed  $value
273
     */
274 8
    public function __set($name, $value)
275
    {
276 8
        $this->dataSet = true;
277 8
        $this->modelData[Text::deCamelize($name)] = $value;
278 8
    }
279
280 12
    public function __get($name)
281
    {
282 12
        return $this->retrieveItem($name);
283
    }
284
285 10
    public function save()
286
    {
287 10
        $return = $this->__call('save', [$this->hasMultipleItems()]);
288 10
        $this->invalidFields = $this->dynamicOperations->getInvalidFields();
289
290 10
        return $return;
291
    }
292
293 18
    private function hasMultipleItems()
294
    {
295 18
        if (count($this->modelData) > 0) {
296 16
            return is_numeric(array_keys($this->modelData)[0]);
297
        } else {
298 2
            return false;
299
        }
300
    }
301
302 18
    public function getData()
303
    {
304 18
        $data = [];
305
306 18
        if (count($this->modelData) == 0) {
307 6
            $data = $this->modelData;
308 16
        } elseif ($this->hasMultipleItems()) {
309 6
            $data = $this->modelData;
310 12
        } elseif (count($this->modelData) > 0) {
311 12
            $data[] = $this->modelData;
312
        }
313
314 18
        return $data;
315
    }
316
317 26
    public function setData($data)
318
    {
319 26
        $this->dataSet = is_array($data) ? true : false;
320 26
        $this->modelData = $data;
321 26
    }
322
323
    public function mergeData($data)
324
    {
325
        foreach ($data as $key => $value) {
326
            $this->modelData[$key] = $value;
327
        }
328
        $this->dataSet = true;
329
    }
330
331 2
    public function offsetExists($offset)
332
    {
333 2
        return isset($this->modelData[$offset]);
334
    }
335
336 2
    public function offsetGet($offset)
337
    {
338 2
        if (is_numeric($offset)) {
339 2
            return $this->wrap($offset);
340
        } else {
341 2
            return $this->retrieveItem($offset);
342
        }
343
    }
344
345 2
    public function offsetSet($offset, $value)
346
    {
347 2
        $this->dataSet = true;
348 2
        $this->modelData[$offset] = $value;
349 2
    }
350
351
    public function offsetUnset($offset)
352
    {
353
        unset($this->modelData[$offset]);
354
    }
355
356 6
    private function wrap($offset)
357
    {
358 6
        $this->initialize();
359 6
        if (isset($this->modelData[$offset])) {
360 6
            $className = $this->className;
361 6
            $newInstance = new $className();
362 6
            $newInstance->initialize();
363 6
            $newInstance->setData($this->modelData[$offset]);
364
365 6
            return $newInstance;
366
        } else {
367
            return;
368
        }
369
    }
370
371 4
    public function getInvalidFields()
372
    {
373 4
        return $this->invalidFields;
374
    }
375
376
    public function getHasMany()
377
    {
378
        return $this->hasMany;
379
    }
380
381
    public function getBelongsTo()
382
    {
383
        return $this->belongsTo;
384
    }
385
386 4
    public function current()
387
    {
388 4
        return $this->wrap($this->keys[$this->index]);
389
    }
390
391
    public function key()
392
    {
393
        return $this->keys[$this->index];
394
    }
395
396 4
    public function next()
397
    {
398 4
        $this->index++;
399 4
    }
400
401 4
    public function rewind()
402
    {
403 4
        $this->keys = array_keys($this->modelData);
404 4
        $this->index = 0;
405 4
    }
406
407 4
    public function valid()
408
    {
409 4
        return isset($this->keys[$this->index]) && isset($this->modelData[$this->keys[$this->index]]);
410
    }
411
412
    /**
413
     * A custom validator for the record wrapper.
414
     *
415
     * @return mixed
416
     */
417 10
    public function validate()
418
    {
419 10
        return [];
420
    }
421
422 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...
423
    {
424 12
        if ($index === null) {
425 12
            $data = $this->modelData;
426
        } else {
427
            $data = $this->modelData[$index];
428
        }
429 12
        $model = $relationship->getModelInstance();
430 12
        if (empty($data)) {
431
            return $model;
432
        }
433 12
        $query = $relationship->prepareQuery($data);
434
435 12
        return $query ? $model->fetch($query) : $model;
436
    }
437
438 28
    public function getRelationships()
439
    {
440
        return [
441 28
            'HasMany'      => $this->hasMany,
442 28
            'BelongsTo'    => $this->belongsTo,
443 28
            'ManyHaveMany' => $this->manyHaveMany,
444
        ];
445
    }
446
447
    public function usetField($field)
448
    {
449
        unset($this->modelData[$field]);
450
    }
451
452
    /**
453
     * Callback for when a record is either added or modified.
454
     */
455 10
    public function preSaveCallback()
456
    {
457 10
    }
458
459
    /**
460
     * Callback for when a record has been added or modified.
461
     *
462
     * @param $id
463
     */
464 6
    public function postSaveCallback()
465
    {
466 6
    }
467
468
    /**
469
     * Callback for when a new record is about to be created.
470
     */
471 8
    public function preCreateCallback()
472
    {
473 8
    }
474
475
    /**
476
     * Callback for when a new record has been created.
477
     * This callback can be most useful for obtaining the primary key of a newly created record.
478
     *
479
     * @param $id
480
     */
481 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...
482
    {
483 4
    }
484
485
    /**
486
     * Callback for when a record is about to be updated.
487
     */
488 2
    public function preUpdateCallback()
489
    {
490 2
    }
491
492
    /**
493
     * Callback for when a record has been updated.
494
     */
495 2
    public function postUpdateCallback()
496
    {
497 2
    }
498
499 36
    public function getDBStoreInformation()
500
    {
501 36
        $this->initialize();
502
503
        return [
504 36
            'schema'         => $this->schema,
505 36
            'table'          => $this->table,
506 36
            'quoted_table'   => $this->quotedTable,
507 36
            'unquoted_table' => $this->unquotedTable,
508
        ];
509
    }
510
511
    /**
512
     * @return DriverAdapter
513
     */
514 36
    public function getAdapter()
515
    {
516 36
        $this->initialize();
517
518 36
        return $this->adapter;
519
    }
520
521 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...
522
    {
523 4
        if (empty($expandableModels)) {
524 4
            foreach ($relationships as $name => $relationship) {
525 4
                $array[$name] = $this->fetchRelatedFields($relationship, $index)->toArray($depth);
526
            }
527
        } else {
528
            foreach ($expandableModels as $name) {
529
                $array[$name] = $this->fetchRelatedFields($relationships[$name], $index)->toArray($depth, $expandableModels);
530
            }
531
        }
532
533 4
        return $array;
534
    }
535
536 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...
537
    {
538 24
        $relationships = $this->getDescription()->getRelationships();
539 24
        $array = $this->modelData;
540 24
        if ($depth > 0) {
541 4
            if ($this->hasMultipleItems()) {
542
                foreach ($array as $i => $value) {
543
                    $array[$i] = $this->expandArrayValue($value, $relationships, $depth - 1, $expandableModels, $i);
544
                }
545
            } else {
546 4
                $array = $this->expandArrayValue($array, $relationships, $depth - 1, $expandableModels);
547
            }
548
        }
549
550 24
        return $array;
551
    }
552
}
553