Record   B
last analyzed

Complexity

Total Complexity 39

Size/Duplication

Total Lines 370
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 99.1%

Importance

Changes 4
Bugs 4 Features 0
Metric Value
wmc 39
c 4
b 4
f 0
lcom 1
cbo 5
dl 0
loc 370
ccs 110
cts 111
cp 0.991
rs 8.2857

16 Methods

Rating   Name   Duplication   Size   Complexity  
A setData() 0 15 4
A toJson() 0 4 1
A getState() 0 4 1
A getTable() 0 4 1
A __construct() 0 8 1
A delete() 0 13 3
B save() 0 47 5
A toArray() 0 7 2
A offsetExists() 0 8 2
A offsetGet() 0 12 3
A offsetSet() 0 14 3
A offsetUnset() 0 15 3
A __set() 0 13 3
A __get() 0 12 3
A setState() 0 5 1
A getRecordPrimaryKeyPredicate() 0 15 3
1
<?php
2
namespace Soluble\Normalist\Synthetic;
3
4
use Zend\Db\Sql\Predicate;
5
use ArrayAccess;
6
use ArrayObject;
7
8
class Record implements ArrayAccess
9
{
10
    /**
11
     * Means record is not yet inserted in database
12
     */
13
    const STATE_NEW         = 'new';
14
15
16
    /**
17
     * Means record have been deleted
18
     */
19
    const STATE_DELETED     = 'deleted';
20
21
    /**
22
     * Means record comes from database
23
     * but one of its data have been modified
24
     */
25
    const STATE_DIRTY       = 'dirty';
26
27
    /**
28
     * Means record comes from database
29
     * but none of its data have been modified
30
     */
31
    const STATE_CLEAN       = 'clean';
32
33
    /**
34
     * @var \ArrayObject
35
     */
36
    protected $_securedFieldForArrayAccess;
37
38
    /**
39
     *
40
     * @var ArrayObject
41
     */
42
    protected $data;
43
44
45
46
    /**
47
     *
48
     * @var string
49
     */
50
    protected $state;
51
52
53
54
    /**
55
     *
56
     * @param array|ArrayObject $data
57
     * @param Table $table
58
     */
59 50
    public function __construct($data, Table $table)
60
    {
61 50
        $this->_securedFieldForArrayAccess = new \ArrayObject();
62 50
        $this->_securedFieldForArrayAccess['table'] = $table;
63 50
        $this->_securedFieldForArrayAccess['state'] = null;
64
        //$this->table = $table;
65 50
        $this->setData($data);
66 49
    }
67
68
    /**
69
     * Delte record
70
     *
71
     * @throws Exception\LogicException if Record has already been deleted
72
     * @return int affected rows can be > 1 if triggers ...
73
     */
74 9
    public function delete()
75
    {
76 9
        $state = $this->getState();
77 9
        if ($state == self::STATE_DELETED) {
78 2
            throw new Exception\LogicException(__METHOD__ . ": Record has already been deleted in database.");
79
        }
80 9
        if ($state == self::STATE_NEW) {
81 2
            throw new Exception\LogicException(__METHOD__ . ": Record has not already been saved in database.");
82
        }
83 7
        $affected_rows = $this->getTable()->deleteBy($this->getRecordPrimaryKeyPredicate());
84 7
        $this->setState(self::STATE_DELETED);
85 7
        return $affected_rows;
86
    }
87
88
    /**
89
     * Save a record in database
90
     *
91
     * @throws Exception\LogicException when record has already been deleted
92
     *
93
     * @param boolean $validate_datatype if true will ensure record data correspond to column datatype
94
     *
95
     * @return Record freshly modified record (from database)
96
     */
97 6
    public function save($validate_datatype = false)
98
    {
99 6
        $state = $this->getState();
100 6
        if ($state == self::STATE_DELETED) {
101 2
            throw new Exception\LogicException(__METHOD__ . "Record has already been deleted in database.");
102
        }
103
104
        switch ($state) {
105 5
            case self::STATE_NEW:
106
                // Means insert
107 4
                $new_record = $this->getTable()->insert($this->toArray(), $validate_datatype);
108 3
                break;
109 3
            case self::STATE_CLEAN:
110 3
            case self::STATE_DIRTY:
111
                // Means update
112 3
                $predicate = $this->getRecordPrimaryKeyPredicate();
113 2
                $this->getTable()->update($this->toArray(), $predicate, $combination = Predicate\PredicateSet::OP_AND, $validate_datatype);
114 1
                $new_record = $this->getTable()->findOneBy($predicate);
115 1
                break;
116
                //@codeCoverageIgnoreStart
117
            default:
118
                throw new Exception\LogicException(__METHOD__ . ": Record is not on manageable state.");
119
                //@codeCoverageIgnoreEnd
120
        }
121
        /*
122
        if ($state == self::STATE_NEW) {
123
            // Means insert
124
            $new_record = $this->getTable()->insert($data, $validate_datatype);
125
126
        } elseif ($state == self::STATE_CLEAN || $state == self::STATE_DIRTY) {
127
            // Means update
128
            $predicate = $this->getRecordPrimaryKeyPredicate();
129
            $this->getTable()->update($data, $predicate, $combination=Predicate\PredicateSet::OP_AND, $validate_datatype);
130
            $new_record = $this->getTable()->findOneBy($predicate);
131
        } else {
132
             //@codeCoverageIgnoreStart
133
134
            throw new Exception\LogicException(__METHOD__ . ": Record is not on manageable state.");
135
             //@codeCoverageIgnoreEnd
136
        }
137
138
         */
139 3
        $this->setData($new_record->toArray());
140 3
        $this->setState(Record::STATE_CLEAN);
141 3
        unset($new_record);
142 3
        return $this;
143
    }
144
145
146
    /**
147
     * Set record data
148
     *
149
     * @param array|ArrayObject $data
150
     * @throws Exception\InvalidArgumentException when the para
151
     * @throws Exception\LogicException when the record has been deleted
152
     * @return Record
153
     */
154 50
    public function setData($data)
155
    {
156 50
        $state = $this->getState();
157 50
        if ($state == self::STATE_DELETED) {
158 1
            throw new Exception\LogicException(__METHOD__ . ": Logic exception, cannot operate on record that was deleted");
159
        }
160 50
        if (is_array($data)) {
161 49
            $data = new ArrayObject($data);
162 50
        } elseif (! $data instanceof ArrayObject) {
163 1
            throw new Exception\InvalidArgumentException(__METHOD__ . ": Data must be an array of an ArrayObject");
164
        }
165 49
        $this->_securedFieldForArrayAccess['data']  = $data;
166 49
        $this->setState(self::STATE_DIRTY);
167 49
        return $this;
168
    }
169
170
    /**
171
     * Return an array version of the record
172
     *
173
     * @throws Exception\LogicException when the record has been deleted
174
     * @return array
175
     */
176 8
    public function toArray()
177
    {
178 8
        if ($this->getState() == self::STATE_DELETED) {
179 1
            throw new Exception\LogicException(__METHOD__ . ": Logic exception, cannot operate on record that was deleted");
180
        }
181 7
        return (array) $this->_securedFieldForArrayAccess['data'];
182
    }
183
184
    /**
185
     * Return an json version of the record
186
     *
187
     * @throws Exception\LogicException when the record has been deleted
188
     * @return string
189
     */
190 1
    public function toJson()
191
    {
192 1
        return json_encode($this->toArray());
193
    }
194
195
196
197
198
    /**
199
     *
200
     * @param string $field
201
     * @return boolean
202
     */
203 2
    public function offsetExists($field)
204
    {
205 2
        if ($this->getState() == self::STATE_DELETED) {
206 1
            throw new Exception\LogicException(__METHOD__ . ": Logic exception, cannot operate on record that was deleted");
207
        }
208
209 1
        return array_key_exists($field, $this->_securedFieldForArrayAccess['data']);
210
    }
211
212
    /**
213
     * Get field value
214
     * @param string $field
215
     * @return mixed
216
     * @throws Exception\FieldNotFoundException
217
     */
218 41
    public function offsetGet($field)
219
    {
220 41
        if ($this->getState() == self::STATE_DELETED) {
221 1
            throw new Exception\LogicException(__METHOD__ . ": Logic exception, cannot operate on record that was deleted");
222
        }
223
224 41
        if (!array_key_exists($field, $this->_securedFieldForArrayAccess['data'])) {
225 3
            throw new Exception\FieldNotFoundException(__METHOD__ . ": Cannot get field value, field '$field' does not exists in record.");
226
        }
227
228 41
        return  $this->_securedFieldForArrayAccess['data'][$field];
229
    }
230
231
    /**
232
     * Set a field
233
     * @param string $field
234
     * @param mixed $value
235
     * @throws Exception\LogicException when the record has been deleted
236
     * @return Record
237
     */
238 6
    public function offsetSet($field, $value)
239
    {
240 6
        $state = $this->getState();
241 6
        if ($state == self::STATE_DELETED) {
242 1
            throw new Exception\LogicException(__METHOD__ . ": Logic exception, cannot operate on record that was deleted");
243
        }
244
245
246 5
        $this->_securedFieldForArrayAccess['data'][$field] = $value;
247 5
        if ($state != self::STATE_NEW) {
248 4
            $this->setState(self::STATE_DIRTY);
249 4
        }
250 5
        return $this;
251
    }
252
253
    /**
254
     * Unset a field
255
     *
256
     * @param string $field
257
     * @throws Exception\LogicException when the record has been deleted
258
     * @return Record
259
     */
260 2
    public function offsetUnset($field)
261
    {
262 2
        $state = $this->getState();
263 2
        if ($state == self::STATE_DELETED) {
264 1
            throw new Exception\LogicException(__METHOD__ . ": Logic exception, cannot operate on record that was deleted");
265
        }
266
267 1
        unset($this->_securedFieldForArrayAccess['data'][$field]);
268
269 1
        if ($state != self::STATE_NEW) {
270 1
            $this->setState(self::STATE_DIRTY);
271 1
        }
272
273 1
        return $this;
274
    }
275
276
277
    /**
278
     * Magic setter
279
     *
280
     * @throws Exception\LogicException when the record has been deleted
281
     * @param string $field
282
     * @param mixed $value
283
     * @return void
284
     */
285
286 2
    public function __set($field, $value)
287
    {
288 2
        $state = $this->getState();
289 2
        if ($state == self::STATE_DELETED) {
290 1
            throw new Exception\LogicException(__METHOD__ . ": Logic exception, cannot operate on record that was deleted");
291
        }
292
293
294 1
        $this->_securedFieldForArrayAccess['data'][$field] = $value;
295 1
        if ($state != self::STATE_NEW) {
296 1
            $this->setState(self::STATE_DIRTY);
297 1
        }
298 1
    }
299
300
    /**
301
     * Magical getter
302
     *
303
     * @param string $field
304
     * @throws Exception\FieldNotFoundException
305
     * @throws Exception\LogicException when the record has been deleted
306
     * @return mixed
307
     */
308 4
    public function __get($field)
309
    {
310 4
        if ($this->getState() == self::STATE_DELETED) {
311 1
            throw new Exception\LogicException(__METHOD__ . ": Logic exception, cannot operate on record that was deleted");
312
        }
313
314 3
        if (!array_key_exists($field, $this->_securedFieldForArrayAccess['data'])) {
315 1
            throw new Exception\FieldNotFoundException(__METHOD__ . ": Cannot get field value, field '$field' does not exists in record.");
316
        }
317
318 3
        return  $this->_securedFieldForArrayAccess['data'][$field];
319
    }
320
321
322
323
    /**
324
     * For internal use only
325
     *
326
     * @param string $state
327
     * @return Record
328
     */
329 49
    public function setState($state)
330
    {
331 49
        $this->_securedFieldForArrayAccess['state'] = $state;
332 49
        return $this;
333
    }
334
335
    /**
336
     *
337
     * @return string
338
     */
339 50
    public function getState()
340
    {
341 50
        return $this->_securedFieldForArrayAccess['state'];
342
    }
343
344
345
    /**
346
     * Return primary key predicate on record
347
     *
348
     *
349
     * @throws Exception\PrimaryKeyNotFoundException
350
     * @throws Exception\UnexcpectedValueException
351
     * @return array predicate
352
     */
353 10
    protected function getRecordPrimaryKeyPredicate()
354
    {
355
        // Get table primary keys
356 10
        $primary_keys = $this->getTable()->getPrimaryKeys();
357 10
        $predicate = [];
358 10
        foreach ($primary_keys as $column) {
359 10
            $pk_value = $this->offsetGet($column);
360 10
            if ($pk_value != '') {
361 9
                $predicate[$column] = $pk_value;
362 9
            } else {
363 1
                throw new Exception\UnexpectedValueException(__METHOD__ . ": Cannot find record primary key values. Record has no primary key value set");
364
            }
365 9
        }
366 9
        return $predicate;
367
    }
368
369
    /**
370
     * Return originating table
371
     * @return Table
372
     */
373 12
    public function getTable()
374
    {
375 12
        return $this->_securedFieldForArrayAccess['table'];
376
    }
377
}
378