Completed
Push — master ( 343d0a...c5b636 )
by James Ekow Abaka
02:58
created

DataOperations::getInvalidFields()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
ccs 2
cts 2
cp 1
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
/**
30
 * Description of DataOperations
31
 *
32
 * @author ekow
33
 */
34
class DataOperations {
35
36
    private $wrapper;
37
38
    /**
39
     *
40
     * @var DriverAdapter
41
     */
42
    private $adapter;
43
    private $data;
44
    private $invalidFields;
45
    private $hasMultipleData;
46
    private $driver;
47
    private $container;
48
49
    const MODE_SAVE = 0;
50
    const MODE_UPDATE = 1;
51
52 35 View Code Duplication
    public function __construct(ORMContext $context, RecordWrapper $wrapper, DriverAdapter $adapter) {
53 35
        $this->wrapper = $wrapper;
54 35
        $this->adapter = $adapter;
55 35
        $this->driver = $context->getDbContext()->getDriver();
56 35
        $this->container = $context->getContainer();
57 35
    }
58
59 10
    public function doSave($hasMultipleData) {
60 10
        $this->hasMultipleData = $hasMultipleData;
61 10
        $invalidFields = [];
62 10
        $data = $this->wrapper->getData();
63 10
        $primaryKey = $this->wrapper->getDescription()->getPrimaryKey();
64 10
        $singlePrimaryKey = null;
0 ignored issues
show
Unused Code introduced by
$singlePrimaryKey is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
65 10
        $succesful = true;
66
67 10
        if (count($primaryKey) == 1) {
68 10
            $singlePrimaryKey = $primaryKey[0];
0 ignored issues
show
Unused Code introduced by
$singlePrimaryKey is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
69
        }
70
71
        // Assign an empty array to force a validation error for empty models
72 10
        if (empty($data)) {
73 2
            $data = [[]];
74
        }
75
76 10
        $this->driver->beginTransaction();
77
78 10
        foreach ($data as $i => $datum) {
79 10
            $status = $this->saveRecord($datum, $primaryKey);
0 ignored issues
show
Documentation introduced by
$primaryKey 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...
80 10
            $data[$i] = $datum;
81
82 10
            if (!$status['success']) {
83 4
                $succesful = false;
84 4
                $invalidFields[$i] = $status['invalid_fields'];
85 4
                $this->driver->rollback();
86 10
                break;
87
            }
88
        }
89
90 10
        if ($succesful) {
91 6
            $this->driver->commit();
92
        } else {
93 4
            $this->assignValue($this->invalidFields, $invalidFields);
94
        }
95
96 10
        $this->wrapper->setData($hasMultipleData ? $data : $data[0]);
97
98 10
        return $succesful;
99
    }
100
    
101
    public function doValidate() {
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...
102
        $record = $this->wrapper->getData()[0];
103
        $primaryKey = $this->wrapper->getDescription()->getPrimaryKey();
104
        $pkSet = $this->isPrimaryKeySet($primaryKey, $record);
105
        return $this->validate(
106
            $record, $pkSet ? DataOperations::MODE_UPDATE : DataOperations::MODE_SAVE
107
        );
108
    }
109
110
    /**
111
     * Save an individual record.
112
     * 
113
     * @param array $record The record to be saved
114
     * @param type $primaryKey The primary keys of the record
115
     * @return boolean
0 ignored issues
show
Documentation introduced by
Should the return type not be array?

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...
116
     */
117 10
    private function saveRecord(&$record, $primaryKey) {
118
        $status = [
119 10
            'success' => true,
120
            'pk_assigned' => null,
121
            'invalid_fields' => []
122
        ];
123
124
        // Determine if the primary key of the record is set.
125 10
        $pkSet = $this->isPrimaryKeySet($primaryKey, $record);
126
127
        // Reset the data in the model to contain only the data to be saved
128 10
        $this->wrapper->setData($record);
129
130
        // Run preUpdate or preSave callbacks on models and behaviours
131 10
        if ($pkSet) {
132 2
            $this->wrapper->preUpdateCallback();
133 2
            $record = $this->wrapper->getData();
134 2
            $record = reset($record) === false ? [] : reset($record);
135 2
            $record = $this->runBehaviours('preUpdateCallback', [$record]);
136
        } else {
137 8
            $this->wrapper->preSaveCallback();
138 8
            $record = $this->wrapper->getData();
139 8
            $record = reset($record) === false ? [] : reset($record);
140 8
            $record = $this->runBehaviours('preSaveCallback', [$record]);
141
        }
142
143
        // Validate the data
144 10
        $validity = $this->validate(
145 10
            $record, $pkSet ? DataOperations::MODE_UPDATE : DataOperations::MODE_SAVE
146
        );
147
148
        // Exit if data is invalid
149 10
        if ($validity !== true) {
150 4
            $status['invalid_fields'] = $validity;
151 4
            $status['success'] = false;
152 4
            return $status;
153
        }
154
        
155
        // Save any relationships that are attached to the data
156 6
        $relationships = $this->wrapper->getDescription()->getRelationships();
157 6
        $presentRelationships = [];
158
        
159 6
        foreach($relationships ?? [] as $model => $relationship) {
160 6
            if(isset($record[$model])) {
161
                $relationship->preSave($record, $record[$model]);
162 6
                $presentRelationships[$model] = $relationship;
163
            }
164
        }
165
        
166
        // Assign the data to the wrapper again
167 6
        $this->wrapper->setData($record);
168
169
        // Update or save the data and run post callbacks
170 6
        if ($pkSet) {
171 2
            $this->adapter->update($record);
172 2
            $this->wrapper->postUpdateCallback();
173 2
            $this->runBehaviours('postUpdateCallback', [$record]);
174
        } else {
175 4
            $this->adapter->insert($record);
176 4
            $keyValue = $this->driver->getLastInsertId();
177 4
            $this->wrapper->{$primaryKey[0]} = $keyValue;
178 4
            $this->wrapper->postSaveCallback($keyValue);
179 4
            $this->runBehaviours('postSaveCallback', [$record, $keyValue]);
180
        }
181
        
182 6
        foreach($presentRelationships as $model => $relationship) {
183
            $relationship->postSave($record);
184
        }        
185
186
        // Reset the data so it contains any modifications made by callbacks
187 6
        $record = $this->wrapper->getData()[0];
188 6
        return $status;
189
    }
190
191 10
    private function validate($data, $mode) {
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...
192 10
        $valid = true;
193 10
        $validator = $this->container->resolve(
194 10
            ModelValidator::class, ['model' => $this->wrapper, 'mode' => $mode]
195
        );
196
197 10
        if (!$validator->validate($data)) {
198 4
            $valid = false;
199
        }
200
201 10
        if ($valid) {
202 6
            $valid = $this->wrapper->onValidate();
203
        }
204
205 10
        if ($valid === false) {
206 4
            $valid = $validator->getInvalidFields();
207
        }
208
209 10
        return $valid;
210
    }
211
212 10
    private function isPrimaryKeySet($primaryKey, $data) {
213 10
        if (is_string($primaryKey) && ($data[$primaryKey] !== null || $data[$primaryKey] !== '')) {
214
            return true;
215
        }
216 10
        foreach ($primaryKey as $keyField) {
217 10
            if (!isset($data[$keyField]) || $data[$keyField] === null || $data[$keyField] === '') {
218 10
                return false;
219
            }
220
        }
221 2
        return true;
222
    }
223
224 4
    private function assignValue(&$property, $value) {
225 4
        if ($this->hasMultipleData) {
226
            $property = $value;
227
        } else {
228 4
            $property = $value[0];
229
        }
230 4
    }
231
232
    public function getData() {
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...
233
        return $this->data;
234
    }
235
236 10
    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...
237 10
        return $this->invalidFields;
238
    }
239
240
    public function isItemDeletable($primaryKey, $data) {
241
        if ($this->isPrimaryKeySet($primaryKey, $data)) {
242
            return true;
243
        } else {
244
            return false;
245
        }
246
    }
247
248 10
    private function runBehaviours($event, $args) {
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...
249 10
        foreach ($this->wrapper->getBehaviours() as $behaviour) {
250
            $args[0] = call_user_func_array([$behaviour, $event], $args);
251
        }
252 10
        return $args[0];
253
    }
254
255
}
256