Completed
Push — master ( a21b6d...7da92d )
by James Ekow Abaka
02:52
created

RecordWrapper::next()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
/*
4
 * The MIT License
5
 *
6
 * Copyright 2015 ekow.
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
 * 
33
 */
34
class RecordWrapper implements \ArrayAccess, \Countable, \Iterator {
35
36
    use \ntentan\panie\ComponentContainerTrait;
37
38
    protected $hasMany = [];
39
    protected $belongsTo = [];
40
    protected $manyHaveMany = [];
41
    protected $behaviours = [];
42
    protected $table;
43
    protected $schema;
44
    private $quotedTable;
45
    private $unquotedTable;
46
    private $modelData = [];
47
    private $invalidFields;
48
    private $dynamicOperations;
49
    private $index = 0;
50
    private $dataSet = false;
51
    protected $adapter;
52
    private $container;
53
    private $context;
54
55 37
    public function __construct(DriverAdapter $adapter, ORMContext $context) {
56 37
        $table = $context->getModelTable($this);
57 37
        $driver = $context->getDbContext()->getDriver();
58 37
        $adapter->setContext($context);
59 37
        $this->container = $context->getContainer();
60 37
        $this->context = $context;
61 37
        if (is_string($table)) {
62 36
            $this->quotedTable = $driver->quoteIdentifier($table);
63 36
            $this->table = $this->unquotedTable = $table;
64
        } else {
65 1
            $this->quotedTable = (isset($table['schema']) ? "{$driver->quoteIdentifier($table["schema"])}." : "").$driver->quoteIdentifier($table["table"]);
66 1
            $this->unquotedTable = (isset($table['schema']) ? "{$table['schema']}." : "").$table['table'];
67 1
            $this->table = $table['table'];
68 1
            $this->schema = $table['schema'];
69
        }
70 37
        $this->adapter = $adapter;
71 37
        $this->adapter->setModel($this, $this->quotedTable);
0 ignored issues
show
Unused Code introduced by
The call to DriverAdapter::setModel() has too many arguments starting with $this->quotedTable.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
72 37
        foreach ($this->behaviours as $behaviour) {
73
            $behaviourInstance = $this->getComponentInstance($behaviour);
0 ignored issues
show
Bug introduced by
The call to getComponentInstance() misses a required argument $component.

This check looks for function calls that miss required arguments.

Loading history...
74
            $behaviourInstance->setModel($this);
75
        }
76 37
    }
77
    
78
    public function __debugInfo() {
79
        return $this->getData();
80
    }
81
82
    /**
83
     * 
84
     * @return ModelDescription
85
     */
86 28
    public function getDescription() {
87 28
        return $this->context->getCache()->read(
88 28
            (new \ReflectionClass($this))->getName().'::desc', function() {
89 28
                return $this->container->resolve(ModelDescription::class, ['model' => $this]);
90 28
            }
91
        );
92
    }
93
94
    /**
95
     * Return the number of items stored in the model or the query.
96
     * @return integer
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...
97
     */
98 14
    public function count() {
99 14
        if ($this->dataSet) {
100 14
            return count($this->getData());
101
        } else {
102
            return $this->__call('count', []);
0 ignored issues
show
Documentation introduced by
'count' is of type string, but the function expects a object<ntentan\nibii\type>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
array() is of type array, but the function expects a object<ntentan\nibii\type>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
103
        }
104
    }
105
106
    /**
107
     * Retrieve an item stored in the record.
108
     * This method returns items that are directly stored in the model or lazy
109
     * loads related items. The key could be a field in the model's table or
110
     * the name of a related model.
111
     * @param string $key A key identifying the item to be retrieved.
112
     * @return mixed
113
     */
114 12
    private function retrieveItem($key) {
115 12
        $relationships = $this->getDescription()->getRelationships();
116 12
        $decamelizedKey = Text::deCamelize($key);
117 12
        if (isset($relationships[$key])) {
118 8
            return $this->fetchRelatedFields($relationships[$key]);
119
        }
120 4
        return isset($this->modelData[$decamelizedKey]) ? $this->modelData[$decamelizedKey] : null;
121
    }
122
123
    /**
124
     * Create a new instance of this Model
125
     * @return \ntentan\nibii\RecordWrapper
126
     */
127 35
    public static function createNew() {
128 35
        $class = get_called_class();
129 35
        return ORMContext::getInstance()->getContainer()->resolve($class);
130
    }
131
132
    /**
133
     * @method
134
     * @param type $name
135
     * @param type $arguments
136
     * @return type
137
     */
138 35
    public function __call($name, $arguments) {
139 35
        if ($this->dynamicOperations === null) {
140
            // Bind to existing instances
141 35
            $this->container->bind(RecordWrapper::class)->to($this);
142 35
            $this->dynamicOperations = $this->container->resolve(
143 35
                Operations::class, ['table' => $this->quotedTable, 'adapter' => $this->adapter]
144
            );
145
            // Unbind all bindings (necessary?)
146
        }
147 35
        return $this->dynamicOperations->perform($name, $arguments);
148
    }
149
150 27
    public static function __callStatic($name, $arguments) {
151 27
        return call_user_func_array([self::createNew(), $name], $arguments);
152
    }
153
154 8
    public function __set($name, $value) {
155 8
        $this->dataSet = true;
156 8
        $this->modelData[Text::deCamelize($name)] = $value;
157 8
    }
158
159 12
    public function __get($name) {
160 12
        return $this->retrieveItem($name);
161
    }
162
163 4
    private function expandArrayValue($array, $relationships, $depth, $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...
164 4
        foreach ($relationships as $name => $relationship) {
165 4
            $array[$name] = $this->fetchRelatedFields($relationship, $index)->toArray($depth);
166
        }
167 4
        return $array;
168
    }
169
170 16
    public function toArray($depth = 0) {
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...
171 16
        $relationships = $this->getDescription()->getRelationships();
172 16
        $array = $this->modelData;
173 16
        if ($depth > 0) {
174 4
            if ($this->hasMultipleData()) {
175
                foreach ($array as $i => $value) {
176
                    $array[$i] = $this->expandArrayValue($value, $relationships, $depth - 1, $i);
177
                }
178
            } else {
179 4
                $array = $this->expandArrayValue($array, $relationships, $depth - 1);
180
            }
181
        }
182 16
        return $array;
183
    }
184
185 10
    public function save() {
186 10
        $return = $this->__call('save', [$this->hasMultipleData()]);
0 ignored issues
show
Documentation introduced by
'save' is of type string, but the function expects a object<ntentan\nibii\type>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
array($this->hasMultipleData()) is of type array<integer,boolean,{"0":"boolean"}>, but the function expects a object<ntentan\nibii\type>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
187 10
        $this->invalidFields = $this->dynamicOperations->getInvalidFields();
188 10
        return $return;
189
    }
190
191 18
    private function hasMultipleData() {
192 18
        if (count($this->modelData) > 0) {
193 16
            return is_numeric(array_keys($this->modelData)[0]);
194
        } else {
195 2
            return false;
196
        }
197
    }
198
199 18
    public function getData() {
200 18
        $data = [];
201
202 18
        if (count($this->modelData) == 0) {
203 6
            $data = $this->modelData;
204 16
        } else if ($this->hasMultipleData()) {
205 6
            $data = $this->modelData;
206 12
        } else if (count($this->modelData) > 0) {
207 12
            $data[] = $this->modelData;
208
        }
209
210 18
        return $data;
211
    }
212
213 27
    public function setData($data) {
214 27
        $this->dataSet = true;
215 27
        $this->modelData = $data;
216 27
    }
217
218
    public function mergeData($data) {
219
        foreach ($data as $key => $value) {
220
            $this->modelData[$key] = $value;
221
        }
222
        $this->dataSet = true;
223
    }
224
225 2
    public function offsetExists($offset) {
226 2
        return isset($this->modelData[$offset]);
227
    }
228
229 2
    public function offsetGet($offset) {
230 2
        if (is_numeric($offset)) {
231 2
            return $this->wrap($offset);
232
        } else {
233 2
            return $this->retrieveItem($offset);
234
        }
235
    }
236
237 2
    public function offsetSet($offset, $value) {
238 2
        $this->dataSet = true;
239 2
        $this->modelData[$offset] = $value;
240 2
    }
241
242
    public function offsetUnset($offset) {
243
        unset($this->modelData[$offset]);
244
    }
245
246 6
    private function wrap($offset) {
247 6
        if (isset($this->modelData[$offset])) {
248 6
            $newInstance = $this->createNew();
249 6
            $newInstance->setData($this->modelData[$offset]);
250 6
            return $newInstance;
251
        } else {
252
            return null;
253
        }
254
    }
255
256 4
    public function getInvalidFields() {
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...
257 4
        return $this->invalidFields;
258
    }
259
260
    public function getHasMany() {
261
        return $this->hasMany;
262
    }
263
264
    public function getBelongsTo() {
265
        return $this->belongsTo;
266
    }
267
268 4
    public function current() {
269 4
        return $this->wrap($this->index);
270
    }
271
272
    public function key() {
273
        return $this->index;
274
    }
275
276 4
    public function next() {
277 4
        $this->index++;
278 4
    }
279
280 4
    public function rewind() {
281 4
        $this->index = 0;
282 4
    }
283
284 4
    public function valid() {
285 4
        return isset($this->modelData[$this->index]);
286
    }
287
288 6
    public function onValidate() {
289 6
        return true;
290
    }
291
292 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...
293 12
        if ($index === null) {
294 12
            $data = $this->modelData;
295
        } else {
296
            $data = $this->modelData[$index];
297
        }
298 12
        $model = $relationship->getModelInstance();
299 12
        if (empty($data)) {
300
            return $model;
301
        }
302 12
        $query = $relationship->getQuery($data);
303 12
        return $query ? $model->fetch($query) : $model;
304
    }
305
306 28
    public function getRelationships() {
307
        return [
308 28
            'HasMany' => $this->hasMany,
309 28
            'BelongsTo' => $this->belongsTo,
310 28
            'ManyHaveMany' => $this->manyHaveMany
311
        ];
312
    }
313
314
    public function usetField($field) {
315
        unset($this->modelData[$field]);
316
    }
317
318 8
    public function preSaveCallback() {
319
        
320 8
    }
321
322 4
    public function postSaveCallback($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...
323
        
324 4
    }
325
326 2
    public function preUpdateCallback() {
327
        
328 2
    }
329
330 2
    public function postUpdateCallback() {
331
        
332 2
    }
333
334 10
    public function getBehaviours() {
335 10
        return $this->loadedComponents;
336
    }
337
338 37
    public function getDBStoreInformation() {
339
        return [
340 37
            'schema' => $this->schema,
341 37
            'table' => $this->table,
342 37
            'quoted_table' => $this->quotedTable,
343 37
            'unquoted_table' => $this->unquotedTable
344
        ];
345
    }
346
347
}
348