Completed
Pull Request — master (#2)
by René
16:36 queued 06:34
created

Entity   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 246
Duplicated Lines 3.66 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 24
c 2
b 0
f 0
lcom 1
cbo 3
dl 9
loc 246
rs 10

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getColumns() 0 13 1
A set() 3 20 4
A get() 3 8 2
C validatePropertyValueType() 3 49 7
A isAltered() 0 4 1
A setUnaltered() 0 4 1
A getAlteredColumns() 0 4 1
A toArray() 0 10 2
A alteredToArray() 0 10 2
A toArrayFromColumns() 0 15 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
declare(strict_types = 1);
3
4
namespace Zortje\MVC\Model\Table\Entity;
5
6
use Zortje\MVC\Model\Table\Entity\Exception\InvalidEntityPropertyException;
7
use Zortje\MVC\Model\Table\Entity\Exception\InvalidValueTypeForEntityPropertyException;
8
9
/**
10
 * Class Entity
11
 *
12
 * @package Zortje\MVC\Model\Table\Entity
13
 */
14
abstract class Entity
15
{
16
17
    /**
18
     * @var array Columns
19
     */
20
    protected static $columns = [];
21
22
    /**
23
     * @var array Internal entity properties
24
     */
25
    protected $properties = [];
26
27
    /**
28
     * @var array Internal altered columns
29
     */
30
    protected $alteredColumns = [];
31
32
    /**
33
     * @param int|null  $id       Entity ID
34
     * @param \DateTime $modified Datetime of last modification
35
     * @param \DateTime $created  Datetime of creation
36
     */
37
    public function __construct($id, \DateTime $modified, \DateTime $created)
38
    {
39
        $this->set('id', $id);
40
        $this->set('modified', $modified);
41
        $this->set('created', $created);
42
    }
43
44
    /**
45
     * Get entity columns
46
     *
47
     * @return array Entity columns
48
     */
49
    public static function getColumns(): array
50
    {
51
        $columns = array_merge([
52
            'id' => 'integer'
53
        ], static::$columns);
54
55
        $columns = array_merge($columns, [
56
            'modified' => 'datetime',
57
            'created'  => 'datetime'
58
        ]);
59
60
        return $columns;
61
    }
62
63
    /**
64
     * Set entity property
65
     *
66
     * @param string                                      $key   Entity property name
67
     * @param object|int|double|string|array|boolean|null $value Entity property value
68
     *
69
     * @throws InvalidEntityPropertyException If entity does not have that property
70
     * @throws InvalidValueTypeForEntityPropertyException If value is of the wrong type
71
     */
72
    public function set(string $key, $value)
73
    {
74 View Code Duplication
        if (isset(self::getColumns()[$key]) === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
75
            throw new InvalidEntityPropertyException([get_class($this), $key]);
76
        }
77
78
        $newValue = $this->validatePropertyValueType($key, $value);
79
80
        if (!isset($this->properties[$key]) || $this->properties[$key] !== $newValue) {
81
            /**
82
             * Set internal property
83
             */
84
            $this->properties[$key] = $newValue;
85
86
            /**
87
             * Set altered column
88
             */
89
            $this->alteredColumns[$key] = true;
90
        }
91
    }
92
93
    /**
94
     * Get entity property
95
     *
96
     * @param string $key Entity property
97
     *
98
     * @return object|int|double|string|array|boolean|null Entity property value for given key
99
     *
100
     * @throws InvalidEntityPropertyException If entity does not have that property
101
     */
102
    public function get(string $key)
103
    {
104 View Code Duplication
        if (!isset(self::getColumns()[$key])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
105
            throw new InvalidEntityPropertyException([get_class($this), $key]);
106
        }
107
108
        return $this->properties[$key];
109
    }
110
111
    /**
112
     * Validate type for given property value
113
     *
114
     * @param string $key   Entity property name
115
     * @param mixed  $value Entity property value
116
     *
117
     * @return object|int|double|string|array|boolean|null Value
118
     *
119
     * @throws InvalidEntityPropertyException If entity does not have that property
120
     * @throws InvalidValueTypeForEntityPropertyException If value is of the wrong type
121
     */
122
    public function validatePropertyValueType(string $key, $value)
123
    {
124 View Code Duplication
        if (!isset(self::getColumns()[$key])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
125
            throw new InvalidEntityPropertyException([get_class($this), $key]);
126
        }
127
128
        /**
129
         * Allow NULL
130
         */
131
        if ($value !== null) {
132
            $valueType = strtolower(gettype($value));
133
134
            /**
135
             * Get class if object
136
             */
137
            if ($valueType === 'object') {
138
                $valueType = strtolower(get_class($value));
139
            }
140
141
            /**
142
             * Handle alias types
143
             */
144
            $columnType = self::getColumns()[$key];
145
146
            switch ($columnType) {
147
                case 'date':
148
                    $columnType = 'datetime';
149
                    break;
150
151
                case 'varbinary':
152
                    $columnType = 'string';
153
                    break;
154
            }
155
156
            /**
157
             * Validate type
158
             */
159
            if ($valueType !== $columnType) {
160
                throw new InvalidValueTypeForEntityPropertyException([
161
                    get_class($this),
162
                    $key,
163
                    $columnType,
164
                    $valueType
165
                ]);
166
            }
167
        }
168
169
        return $value;
170
    }
171
172
    /**
173
     * Check if entity has been altered
174
     *
175
     * @return bool True if altered, otherwise false
176
     */
177
    public function isAltered(): bool
178
    {
179
        return count($this->alteredColumns) > 0;
180
    }
181
182
    /**
183
     * Marks the entity as unaltered
184
     */
185
    public function setUnaltered()
186
    {
187
        $this->alteredColumns = [];
188
    }
189
190
    /**
191
     * Get altered columns
192
     *
193
     * @return array
194
     */
195
    public function getAlteredColumns(): array
196
    {
197
        return $this->alteredColumns;
198
    }
199
200
    /**
201
     * Return table structure for saving
202
     * Example: `[':{table_field_name}' => $this->fieldName]`
203
     *
204
     * @param bool $includeId Should the ID column be included
205
     *
206
     * @return array
207
     */
208
    public function toArray(bool $includeId): array
209
    {
210
        $columns = self::getColumns();
211
212
        if (!$includeId) {
213
            unset($columns['id']);
214
        }
215
216
        return $this->toArrayFromColumns($columns);
217
    }
218
219
    /**
220
     * Return table structure for saving just altered columns
221
     *
222
     * @param bool $includeId Should the ID column be included
223
     *
224
     * @return array
225
     */
226
    public function alteredToArray(bool $includeId): array
227
    {
228
        $alteredColumns = $this->alteredColumns;
229
230
        if ($includeId) {
231
            $alteredColumns['id'] = true;
232
        }
233
234
        return $this->toArrayFromColumns(array_intersect_key(self::getColumns(), $alteredColumns));
235
    }
236
237
    /**
238
     * Return columns in structure for saving
239
     *
240
     * @param array $columns Columns to include
241
     *
242
     * @return array
243
     */
244
    protected function toArrayFromColumns(array $columns): array
245
    {
246
        $array = [];
247
248
        foreach ($columns as $column => $type) {
249
            $property = new EntityProperty($type);
250
251
            $value = $this->get($column);
252
            $value = $property->formatValueForDatabase($value);
253
254
            $array[":$column"] = $value;
255
        }
256
257
        return $array;
258
    }
259
}
260