Completed
Pull Request — master (#2)
by René
04:40 queued 02:14
created

EntityProperty   B

Complexity

Total Complexity 52

Size/Duplication

Total Lines 272
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 98.02%

Importance

Changes 0
Metric Value
wmc 52
lcom 1
cbo 6
dl 0
loc 272
ccs 99
cts 101
cp 0.9802
rs 7.9487
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 29 7
A setType() 0 8 2
D validateValue() 0 95 26
C formatValueForEntity() 0 39 12
B formatValueForDatabase() 0 24 5

How to fix   Complexity   

Complex Class

Complex classes like EntityProperty often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use EntityProperty, and based on these observations, apply Extract Interface, too.

1
<?php
2
declare(strict_types = 1);
3
4
namespace Zortje\MVC\Model\Table\Entity;
5
6
use Ramsey\Uuid\Uuid;
7
use Zortje\MVC\Model\Table\Entity\Exception\EntityPropertyTypeNonexistentException;
8
use Zortje\MVC\Model\Table\Entity\Exception\EntityPropertyTypeNotImplementedException;
9
use Zortje\MVC\Model\Table\Entity\Exception\EntityPropertyValueExceedingLengthException;
10
use Zortje\MVC\Model\Table\Entity\Exception\InvalidENUMValueForEntityPropertyException;
11
use Zortje\MVC\Model\Table\Entity\Exception\InvalidUUIDValueForEntityPropertyException;
12
use Zortje\MVC\Model\Table\Entity\Exception\InvalidValueTypeForEntityPropertyException;
13
14
/**
15
 * Class EntityProperty
16
 *
17
 * @package Zortje\MVC\Model\Table\Entity
18
 */
19
class EntityProperty
20
{
21
22
    const STRING = 'string';
23
    const INTEGER = 'integer';
24
    const FLOAT = 'float';
25
    const DOUBLE = 'double';
26
    const BOOL = 'bool';
27
28
    const DATE = 'date';
29
    const DATETIME = 'datetime';
30
31
    const VARBINARY = 'varbinary';
32
33
    const UUID = 'uuid';
34
35
    const ENUM = 'enum';
36
37
    /**
38
     * @var string Entity property type
39
     */
40
    protected $type;
41
42
    /**
43
     * @var int Entity property max length
44
     */
45
    protected $length;
46
47
    /**
48
     * @var array Allowed values
49
     */
50
    protected $values;
51
52
    /**
53
     * @param string|array $type
54
     */
55 13
    public function __construct($type)
56
    {
57 13
        if (is_array($type)) {
58
            /**
59
             * Type
60
             */
61 3
            if (!isset($type['type'])) {
62 1
                throw new \InvalidArgumentException('Index "type" not found in parameter array');
63
            }
64
65 2
            $this->setType($type['type']);
66
67
            /**
68
             * Length
69
             */
70 2
            if (isset($type['length']) && is_numeric($type['length'])) {
71 1
                $this->length = (int)$type['length'];
72
            }
73
74
            /**
75
             * Values
76
             */
77 2
            if (isset($type['values']) && is_array($type['values'])) {
78 2
                $this->values = array_fill_keys($type['values'], true);
79
            }
80
        } else {
81 10
            $this->setType($type);
82
        }
83 12
    }
84
85
    /**
86
     * Set entity property type
87
     *
88
     * @param string $type
89
     *
90
     * @throws EntityPropertyTypeNonexistentException
91
     */
92 13
    protected function setType(string $type)
93
    {
94 13
        if (!defined(EntityProperty::class . '::' . strtoupper($type))) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
95 1
            throw new EntityPropertyTypeNonexistentException([$type]);
96
        }
97
98 12
        $this->type = $type;
99 12
    }
100
101
    /**
102
     * Validate value for entity property
103
     *
104
     * @param mixed $value Entity property value
105
     *
106
     * @return bool TRUE if valid, otherwise FALSE
107
     *
108
     * @throws EntityPropertyTypeNotImplementedException If entity property type is not implemented
109
     * @throws EntityPropertyValueExceedingLengthException If value is exceeding allowed length for entity property
110
     * @throws InvalidENUMValueForEntityPropertyException If ENUM value is invalid for entity property
111
     * @throws InvalidUUIDValueForEntityPropertyException If UUID value is invalid for entity property
112
     * @throws InvalidValueTypeForEntityPropertyException If value is invalid for entity property
113
     */
114 22
    public function validateValue($value): bool
115
    {
116 22
        if (is_null($value)) {
117 1
            return true;
118
        }
119
120 21
        switch ($this->type) {
121 21
            case self::STRING:
122 3
                if (!is_string($value)) {
123 1
                    throw new InvalidValueTypeForEntityPropertyException([$this->type, gettype($value)]);
124
                }
125
126
                /**
127
                 * Check length
128
                 */
129 2
                $length = strlen($value);
130
131 2
                if (!is_null($this->length) && $length > $this->length) {
132 1
                    throw new EntityPropertyValueExceedingLengthException([$value, $this->length]);
133
                }
134
135 1
                break;
136
137 18
            case self::INTEGER:
138 2
                if (!is_int($value)) {
139 1
                    throw new InvalidValueTypeForEntityPropertyException([$this->type, gettype($value)]);
140
                }
141
142 1
                break;
143
144 16
            case self::FLOAT:
145 2
                if (!is_float($value)) {
146 1
                    throw new InvalidValueTypeForEntityPropertyException([$this->type, gettype($value)]);
147
                }
148
149 1
                break;
150
151 14
            case self::DOUBLE:
152 2
                if (!is_double($value)) {
153 1
                    throw new InvalidValueTypeForEntityPropertyException([$this->type, gettype($value)]);
154
                }
155
156 1
                break;
157
158 12
            case self::BOOL:
159 2
                if (!is_bool($value)) {
160 1
                    throw new InvalidValueTypeForEntityPropertyException([$this->type, gettype($value)]);
161
                }
162
163 1
                break;
164
165 10
            case self::DATE:
166 8
            case self::DATETIME:
167 4
                if (!is_object($value) || (is_object($value) && get_class($value) !== \DateTime::class)) {
168 2
                    throw new InvalidValueTypeForEntityPropertyException([$this->type, gettype($value)]);
169
                }
170
171 2
                break;
172
173 6
            case self::VARBINARY:
174
                // @todo Implement this
175
176
                break;
177
178 6
            case self::UUID:
179 3
                if (!is_string($value)) {
180 1
                    throw new InvalidValueTypeForEntityPropertyException(['string', gettype($value)]);
181
                }
182
183
                /**
184
                 * Check UUID
185
                 */
186 2
                if (!Uuid::isValid($value)) {
187 1
                    throw new InvalidUUIDValueForEntityPropertyException([$value]);
188
                }
189
190 1
                break;
191
192 3
            case self::ENUM:
193 3
                if (!is_string($value)) {
194 1
                    throw new InvalidValueTypeForEntityPropertyException(['string', gettype($value)]);
195
                }
196
197
                /**
198
                 * Check values
199
                 */
200 2
                if (!isset($this->values[$value])) {
201 1
                    throw new InvalidENUMValueForEntityPropertyException([$value]);
202
                }
203
204 1
                break;
205
        }
206
207 9
        return true;
208
    }
209
210
    /**
211
     * Format value according to entity property type
212
     *
213
     * @param mixed $value Value
214
     *
215
     * @return mixed Value
216
     *
217
     * @throws EntityPropertyTypeNotImplementedException If entity property type is not implemented
218
     */
219 7
    public function formatValueForEntity($value)
220
    {
221 7
        if (is_null($value)) {
222 1
            return null;
223
        }
224
225 6
        switch ($this->type) {
226 6
            case self::STRING:
227 5
            case self::UUID:
228 5
            case self::ENUM:
229 1
                $value = "$value";
230 1
                break;
231
232 5
            case self::INTEGER:
233 1
                $value = (int)$value;
234 1
                break;
235
236 4
            case self::FLOAT:
237 3
            case self::DOUBLE:
238 1
                $value = (float)$value;
239 1
                break;
240
241 3
            case self::DATE:
242 2
            case self::DATETIME:
243 2
                $value = new \DateTime($value);
244 2
                break;
245
246 1
            case self::VARBINARY:
247
                // @todo Implement this
248
249
                break;
250
251 1
            case self::BOOL:
252 1
                $value = $value === '1';
253 1
                break;
254
        }
255
256 6
        return $value;
257
    }
258
259
    /**
260
     * Format value for insertion into the database
261
     *
262
     * @param mixed $value Value
263
     *
264
     * @return mixed Value
265
     */
266 3
    public function formatValueForDatabase($value)
267
    {
268 3
        switch ($this->type) {
269 3
            case self::DATE:
270
                /**
271
                 * @var \DateTime $value
272
                 */
273 1
                $value = $value->format('Y-m-d');
274 1
                break;
275
276 2
            case self::DATETIME:
277
                /**
278
                 * @var \DateTime $value
279
                 */
280 1
                $value = $value->format('Y-m-d H:i:s');
281 1
                break;
282
283 1
            case self::BOOL:
284 1
                $value = $value ? '1' : '0';
285 1
                break;
286
        }
287
288 3
        return $value;
289
    }
290
}
291