Completed
Push — master ( 145f77...f73370 )
by James Ekow Abaka
04:01
created

DataOperations::isPrimaryKeySet()   B

Complexity

Conditions 7
Paths 9

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 7.392

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 18
ccs 8
cts 10
cp 0.8
rs 8.2222
cc 7
eloc 10
nc 9
nop 2
crap 7.392
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\Utils;
30
use ntentan\atiaa\Db;
31
32
/**
33
 * Description of DataOperations
34
 *
35
 * @author ekow
36
 */
37
class DataOperations
38
{
39
    private $wrapper;
40
    
41
    /**
42
     *
43
     * @var DriverAdapter
44
     */
45
    private $adapter;
46
    private $data;
47
    private $invalidFields;
48
    private $validator;
49
    private $hasMultipleData;
50
    
51
    const MODE_SAVE = 0;
52
    const MODE_UPDATE = 1;
53
    
54 34
    public function __construct($wrapper, $adapter)
55
    {
56 34
        $this->wrapper = $wrapper;
57 34
        $this->adapter = $adapter;        
58 34
    }
59
    
60 10
    public function doSave($hasMultipleData)
61
    {
62 10
        $this->hasMultipleData = $hasMultipleData;
63 10
        $invalidFields = [];
64 10
        $data = $this->wrapper->getData();
65 10
        $this->adapter->setModel($this->wrapper);
66 10
        $primaryKey = $this->wrapper->getDescription()->getPrimaryKey();
67 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...
68 10
        $succesful = true;
69
70 10
        if (count($primaryKey) == 1) {
71 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...
72
        }
73
        
74
        // Assign an empty array to force a validation error for empty models
75 10
        if(empty($data)) {
76 2
            $data = [[]];
77
        }
78
79 10
        Db::getDriver()->beginTransaction();
80
81 10
        foreach($data as $i => $datum) {
82 10
            $status = $this->saveRecord($datum, $primaryKey);
83 10
            $data[$i] = $datum;
84
            
85 10
            if(!$status['success']) {
86 4
                $succesful = false;
87 4
                $invalidFields[$i] = $status['invalid_fields'];
88 4
                Db::getDriver()->rollback();
89 10
                break;
90
            }
91
        }
92
        
93 10
        if($succesful) {
94 6
            Db::getDriver()->commit();
95
        } else {
96 4
            $this->assignValue($this->invalidFields, $invalidFields);
97
        }
98
        
99 10
        $this->wrapper->setData($hasMultipleData ? $data : $data[0]);
100
101 10
        return $succesful;
102
    }   
103
    
104
    /**
105
     * Save an individual record.
106
     * 
107
     * @param array $record The record to be saved
108
     * @param type $primaryKey The primary keys of the record
109
     * @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...
110
     */
111 10
    private function saveRecord(&$record, $primaryKey)
112
    {
113
        $status = [
114 10
            'success' => true,
115
            'pk_assigned' => null,
116
            'invalid_fields' => []
117
        ];
118
        
119
        // Determine if the primary key of the record is set.
120 10
        $pkSet = $this->isPrimaryKeySet($primaryKey, $record);
121
        
122
        // Reset the data in the model to contain only the data to be saved
123 10
        $this->wrapper->setData($record);
124
125
        // Run preUpdate or preSave callbacks on models and behaviours
126 10
        if($pkSet) {
127 2
            $this->wrapper->preUpdateCallback();
128 2
            $record = $this->wrapper->getData();
129 2
            $record = reset($record) === false ? [] : reset($record);
130 2
            $record = $this->runBehaviours('preUpdateCallback', [$record]);
131
        } else {
132 8
            $this->wrapper->preSaveCallback();
133 8
            $record = $this->wrapper->getData();
134 8
            $record = reset($record) === false ? [] : reset($record);
135 8
            $record = $this->runBehaviours('preSaveCallback', [$record]);
136
        }        
137
        
138
        // Validat the data
139 10
        $validity = $this->validate(
140
            $record, 
141 10
            $pkSet ? DataOperations::MODE_UPDATE : DataOperations::MODE_SAVE
142
        );
143
144
        // Exit if data is invalid
145 10
        if($validity !== true) {
146 4
            $status['invalid_fields'] = $validity;
147 4
            $status['success'] = false;
148 4
            return $status;
149
        }
150
        
151
        // Assign the data to the wrapper again
152 6
        $this->wrapper->setData($record);
153
154
        // Update or save the data and run post callbacks
155 6
        if($pkSet) {
156 2
            $this->adapter->update($record);
157 2
            $this->wrapper->postUpdateCallback();
158 2
            $this->runBehaviours('postUpdateCallback', [$record]);
159
        } else {
160 4
            $this->adapter->insert($record);
161 4
            $keyValue = Db::getDriver()->getLastInsertId();
162 4
            $this->wrapper->{$primaryKey[0]} = $keyValue;
163 4
            $this->wrapper->postSaveCallback($keyValue);
164 4
            $this->runBehaviours('postSaveCallback', [$record, $keyValue]);
165
        }
166
        
167
        // Reset the data so it contains any modifications made by callbacks
168 6
        $record = $this->wrapper->getData()[0];
169 6
        return $status;
170
    }
171
    
172 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...
173
    {
174 10
        $valid = true;
175 10
        $validator = Utils::factory($this->validator,
176 10
            function() use($mode) {
177 10
                return new ModelValidator($this->wrapper, $mode);
178 10
            }
179
        );
180
181 10
        if(!$validator->validate($data)) {
182 4
            $valid = false;
183
        }
184
185 10
        if($valid === false) {
186 4
            $valid = $validator->getInvalidFields();
187
        }
188
189 10
        return $valid;
190
    }
191
    
192
193 10
    private function isPrimaryKeySet($primaryKey, $data)
194
    {
195 10
        if(is_string($primaryKey)) {
196
            if(isset($data[$primaryKey]))
197
            {
198
                return true;
199
            }
200
        }
201 10
        foreach($primaryKey as $keyField) {
202 10
            if(!isset($data[$keyField])) {
203 8
                break;
204
            }
205 2
            if($data[$keyField] !== '' && $data[$keyField] !== null) {
206 2
                return true;
207
            }
208
        }
209 8
        return false;
210
    }
211
    
212 4
    private function assignValue(&$property, $value)
213
    {
214 4
        if($this->hasMultipleData) {
215
            $property = $value;
216
        } else {
217 4
            $property = $value[0];
218
        }
219 4
    }    
220
    
221
    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...
222
    {
223
        return $this->data;
224
    }
225
    
226 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...
227
    {
228 10
        return $this->invalidFields;
229
    }   
230
    
231
    public function isItemDeletable($primaryKey, $data)
232
    {
233
        if($this->isPrimaryKeySet($primaryKey, $data)) {
234
            return true;
235
        } else {
236
            return false;
237
        }
238
    }     
239
    
240 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...
241
    {
242 10
        foreach($this->wrapper->getBehaviours() as $behaviour) {
243
            $args[0] = call_user_func_array([$behaviour, $event], $args);
244
        }
245 10
        return $args[0];
246
    }
247
}
248