Completed
Push — master ( fe0287...bef35d )
by James Ekow Abaka
01:41
created

DataOperations::doSave()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 38
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 6

Importance

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