AbstractModel::setField()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 11
nc 4
nop 2
dl 0
loc 15
ccs 12
cts 12
cp 1
crap 4
rs 9.9
c 0
b 0
f 0
1
<?php
2
3
namespace ddlzz\AmoAPI\Model;
4
5
use ddlzz\AmoAPI\Exception\EntityFieldsException;
6
use ddlzz\AmoAPI\Exception\InvalidArgumentException;
7
use ddlzz\AmoAPI\Validator\FieldsValidator;
8
9
/**
10
 * Class AbstractModel.
11
 *
12
 * @author ddlzz
13
 */
14
abstract class AbstractModel implements \ArrayAccess
15
{
16
    /** @var FieldsValidator */
17
    private $fieldsValidator;
18
19
    /** @var string */
20
    protected $requestName = '';
21
22
    /**
23
     * Data container for an ArrayAccess interface implementation.
24
     *
25
     * @var array
26
     */
27
    protected $container = [];
28
29
    /**
30
     * Fields parameters for validation purposes. These are fields that are used in every entity.
31
     *
32
     * @var array
33
     */
34
    private $fieldsParams = [
35
        'id' => [
36
            'type' => 'int',
37
            'required_add' => false,
38
            'required_update' => true,
39
        ],
40
        'responsible_user_id' => [
41
            'type' => 'int',
42
            'required_add' => false,
43
            'required_update' => false,
44
        ],
45
        'created_by' => [
46
            'type' => 'int',
47
            'required_add' => false,
48
            'required_update' => false,
49
        ],
50
        'created_at' => [
51
            'type' => 'int',
52
            'required_add' => true,
53
            'required_update' => false,
54
        ],
55
        'updated_at' => [
56
            'type' => 'int',
57
            'required_add' => false,
58
            'required_update' => true,
59
        ],
60
        'account_id' => [
61
            'type' => 'int',
62
            'required_add' => false,
63
            'required_update' => false,
64
        ],
65
    ];
66
67
    /** @var array */
68
    protected $fieldsParamsAppend = [];
69
70
    /** @var array */
71
    private $aliases = [
72
        'created_at' => 'date_create',
73
        'updated_at' => 'last_modified',
74
        'created_by' => 'created_user_id',
75
    ];
76
77
    /** @var array */
78
    protected $aliasesAppend = [];
79
80
    /**
81
     * Entity data goes here.
82
     *
83
     * @var array
84
     */
85
    private $fields = [];
86
87
    /**
88
     * AbstractModel constructor.
89
     */
90 16
    public function __construct()
91
    {
92
        // Append fields and aliases of abstract parent class with the child entity data.
93 16
        $this->fieldsParams = array_merge($this->fieldsParams, $this->fieldsParamsAppend);
94 16
        $this->aliases = array_merge($this->aliases, $this->aliasesAppend);
95
96 16
        $this->fieldsValidator = new FieldsValidator($this->fieldsParams); // Composition
97 16
    }
98
99
    /**
100
     * @param string $action
101
     *
102
     * @throws InvalidArgumentException
103
     */
104 11
    public function setFields($action)
105
    {
106 11
        if (('add' !== $action) && ('update' !== $action) && ('fill' !== $action)) {
107 1
            throw new InvalidArgumentException("Action \"$action\" is not a proper action parameter");
108
        }
109
110 10
        self::validateDataBeforeSet($this->container);
111 8
        $this->setCreatedAt();
112 8
        $this->container = $this->renameAliases($this->container);
113 8
        $this->fieldsValidator->setAction($action);
114
115 8
        foreach ($this->fieldsParams as $key => $params) {
116 8
            $fieldData = isset($this->container[$key]) ? $this->container[$key] : null;
117 8
            if (($this->fieldsValidator->isValid($key, $fieldData)) && (!empty($fieldData))) {
118 8
                $this->setField($key, $fieldData);
119
            }
120
        }
121 8
    }
122
123
    /**
124
     * @return string
125
     */
126 2
    public function getRequestName()
127
    {
128 2
        return $this->requestName;
129
    }
130
131
    /**
132
     * @return array
133
     */
134 5
    public function getFields()
135
    {
136 5
        return $this->fields;
137
    }
138
139
    /**
140
     * @param array $data
141
     *
142
     * @return AbstractModel
143
     */
144 1
    public function fill(array $data)
145
    {
146 1
        $this->container = $data;
147 1
        $this->setFields('fill');
148
149 1
        return $this;
150
    }
151
152
    /**
153
     * The updated_at field must be greater than the existing one, so
154
     * we update it automatically if none was passed by user.
155
     */
156 1
    public function setUpdatedAt()
157
    {
158 1
        if (isset($this->fields['updated_at']) && ($this->container['updated_at'] === $this->fields['updated_at'])) {
159 1
            $this->container['updated_at'] = time();
160
        }
161 1
    }
162
163 8
    private function setCreatedAt()
164
    {
165 8
        if (!isset($this->container['created_at']) && !isset($this->container['date_create'])) {
166 5
            $this->container['created_at'] = time();
167
        }
168 8
    }
169
170
    /**
171
     * @param array $data
172
     *
173
     * @throws EntityFieldsException
174
     *
175
     * @return bool
176
     */
177 10
    private static function validateDataBeforeSet(array $data)
178
    {
179 10
        if (empty($data)) {
180 1
            throw new EntityFieldsException('Data is empty');
181
        }
182
183 9
        if (count(array_filter(array_keys($data), 'is_string')) < 1) {
184 1
            $message = sprintf('Data is not an associative array: "%s"', var_export($data, true));
185 1
            throw new EntityFieldsException($message);
186
        }
187
188 8
        return true;
189
    }
190
191
    /**
192
     * @param array $data
193
     *
194
     * @return array
195
     */
196 8
    private function renameAliases(array $data)
197
    {
198 8
        foreach ($data as $key => $value) {
199 8
            if (in_array($key, $this->aliases, true)) {
200 1
                $newKey = array_search($key, $this->aliases, true);
201 1
                $data[$newKey] = $data[$key];
202 8
                unset($data[$key]);
203
            }
204
        }
205
206 8
        return $data;
207
    }
208
209
    /**
210
     * @param string $key
211
     * @param mixed  $value
212
     */
213 8
    private function setField($key, $value)
214
    {
215 8
        switch ($this->fieldsParams[$key]['type']) {
216 8
            case 'int':
217 8
                $value = (int) $value;
218 8
                break;
219 1
            case 'string':
220 1
                $value = (string) $value;
221 1
                break;
222 1
            case 'bool':
223 1
                $value = (bool) $value;
224 1
                break;
225
        }
226
227 8
        $this->fields[$key] = $value;
228 8
    }
229
230
    //////////////////////////////////////////////
231
    // The implementation of ArrayAccess interface
232
    //////////////////////////////////////////////
233
234
    /**
235
     * @param mixed $offset
236
     * @param mixed $value
237
     */
238 12
    public function offsetSet($offset, $value)
239
    {
240 12
        if (null === $offset) {
241 1
            $this->container[] = $value;
242
        } else {
243 12
            $this->container[$offset] = $value;
244
        }
245 12
    }
246
247
    /**
248
     * @param mixed $offset
249
     *
250
     * @return bool
251
     */
252 2
    public function offsetExists($offset)
253
    {
254 2
        return isset($this->container[$offset]);
255
    }
256
257
    /**
258
     * @param mixed $offset
259
     */
260 1
    public function offsetUnset($offset)
261
    {
262 1
        unset($this->container[$offset]);
263 1
    }
264
265
    /**
266
     * @param mixed $offset
267
     *
268
     * @return mixed|null
269
     */
270 4
    public function offsetGet($offset)
271
    {
272 4
        return isset($this->fields[$offset]) ? $this->fields[$offset] : null;
273
    }
274
}
275