Completed
Pull Request — master (#2)
by René
05:02 queued 02:37
created

Entity::__construct()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 1
nop 3
crap 2
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\InvalidEntityPropertyException;
8
use Zortje\MVC\Model\Table\Entity\Exception\InvalidValueTypeForEntityPropertyException;
9
10
/**
11
 * Class Entity
12
 *
13
 * @package Zortje\MVC\Model\Table\Entity
14
 */
15
abstract class Entity
16
{
17
18
    /**
19
     * @var array Columns
20
     */
21
    protected static $columns = [];
22
23
    /**
24
     * @var array Internal entity properties
25
     */
26
    protected $properties = [];
27
28
    /**
29
     * @var array Internal altered columns
30
     */
31
    protected $alteredColumns = [];
32
33
    /**
34
     * @param string|null $uuid     Entity ID
35
     * @param \DateTime   $modified Datetime of last modification
36
     * @param \DateTime   $created  Datetime of creation
37
     */
38 2
    public function __construct($uuid, \DateTime $modified, \DateTime $created)
39
    {
40 2
        $this->set('uuid', $uuid ?: Uuid::uuid1()->toString());
41 2
        $this->set('modified', $modified);
42 2
        $this->set('created', $created);
43 2
    }
44
45
    /**
46
     * Get entity columns
47
     *
48
     * @return array Entity columns
49
     */
50 1
    public static function getColumns(): array
51
    {
52 1
        $columns = array_merge([
53
            'uuid' => 'uuid'
54 1
        ], static::$columns);
55
56 1
        $columns = array_merge($columns, [
57 1
            'modified' => 'datetime',
58
            'created'  => 'datetime'
59
        ]);
60
61 1
        return $columns;
62
    }
63
64
    /**
65
     * Set entity property
66
     *
67
     * @param string                                      $key   Entity property name
68
     * @param object|int|double|string|array|boolean|null $value Entity property value
69
     *
70
     * @throws InvalidEntityPropertyException If entity does not have that property
71
     * @throws InvalidValueTypeForEntityPropertyException If value is of the wrong type
72
     */
73 2
    public function set(string $key, $value)
74
    {
75 2 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...
76 1
            throw new InvalidEntityPropertyException([get_class($this), $key]);
77
        }
78
79 2
        $newValue = $this->validatePropertyValueType($key, $value);
80
81 2
        if (!isset($this->properties[$key]) || $this->properties[$key] !== $newValue) {
82
            /**
83
             * Set internal property
84
             */
85 2
            $this->properties[$key] = $newValue;
86
87
            /**
88
             * Set altered column
89
             */
90 2
            $this->alteredColumns[$key] = true;
91
        }
92 2
    }
93
94
    /**
95
     * Get entity property
96
     *
97
     * @param string $key Entity property
98
     *
99
     * @return object|int|double|string|array|boolean|null Entity property value for given key
100
     *
101
     * @throws InvalidEntityPropertyException If entity does not have that property
102
     */
103 2
    public function get(string $key)
104
    {
105 2 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...
106 1
            throw new InvalidEntityPropertyException([get_class($this), $key]);
107
        }
108
109 1
        return $this->properties[$key];
110
    }
111
112
    /**
113
     * Validate type for given property value
114
     *
115
     * @param string $key   Entity property name
116
     * @param mixed  $value Entity property value
117
     *
118
     * @return object|int|double|string|array|boolean|null Value
119
     *
120
     * @throws InvalidEntityPropertyException If entity does not have that property
121
     * @throws InvalidValueTypeForEntityPropertyException If value is of the wrong type
122
     */
123 3
    public function validatePropertyValueType(string $key, $value)
124
    {
125 3 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...
126 1
            throw new InvalidEntityPropertyException([get_class($this), $key]);
127
        }
128
129
        /**
130
         * Allow NULL
131
         */
132 3
        if ($value !== null) {
133 3
            $valueType = strtolower(gettype($value));
134
135
            /**
136
             * Get class if object
137
             */
138 3
            if ($valueType === 'object') {
139 3
                $valueType = strtolower(get_class($value));
140
            }
141
142
            /**
143
             * Handle alias types
144
             */
145 3
            $columnType = self::getColumns()[$key];
146
147
            switch ($columnType) {
148 3
                case 'date':
149 3
                    $columnType = 'datetime';
150 3
                    break;
151
152 3
                case 'varbinary':
153
                    $columnType = 'string';
154
                    break;
155
            }
156
157
            /**
158
             * Handle UUID type
159
             */
160 3
            if ($columnType === 'uuid' && Uuid::isValid($value)) {
161 3
                $valueType = 'uuid';
162
            }
163
164
            /**
165
             * Validate type
166
             */
167 3
            if ($valueType !== $columnType) {
168 1
                throw new InvalidValueTypeForEntityPropertyException([get_class($this), $key, $valueType, $columnType]);
169
            }
170
        }
171
172 3
        return $value;
173
    }
174
175
    /**
176
     * Check if entity has been altered
177
     *
178
     * @return bool True if altered, otherwise false
179
     */
180 1
    public function isAltered(): bool
181
    {
182 1
        return count($this->alteredColumns) > 0;
183
    }
184
185
    /**
186
     * Marks the entity as unaltered
187
     */
188 1
    public function setUnaltered()
189
    {
190 1
        $this->alteredColumns = [];
191 1
    }
192
193
    /**
194
     * Get altered columns
195
     *
196
     * @return array
197
     */
198 1
    public function getAlteredColumns(): array
199
    {
200 1
        return $this->alteredColumns;
201
    }
202
203
    /**
204
     * Return table structure for saving
205
     * Example: `[':{table_field_name}' => $this->fieldName]`
206
     *
207
     * @return array
208
     */
209 1
    public function toArray(): array
210
    {
211 1
        $columns = self::getColumns();
212
213 1
        return $this->toArrayFromColumns($columns);
214
    }
215
216
    /**
217
     * Return table structure for saving just altered columns
218
     *
219
     * @param bool $includeUuid Should the UUID column be included
220
     *
221
     * @return array
222
     */
223 2
    public function alteredToArray(bool $includeUuid): array
224
    {
225 2
        $alteredColumns = $this->alteredColumns;
226
227 2
        if ($includeUuid) {
228 1
            $alteredColumns['uuid'] = true;
229
        }
230
231 2
        return $this->toArrayFromColumns(array_intersect_key(self::getColumns(), $alteredColumns));
232
    }
233
234
    /**
235
     * Return columns in structure for saving
236
     *
237
     * @param array $columns Columns to include
238
     *
239
     * @return array
240
     */
241
    protected function toArrayFromColumns(array $columns): array
242
    {
243
        $array = [];
244
245
        foreach ($columns as $column => $type) {
246
            $property = new EntityProperty($type);
247
248
            $value = $this->get($column);
249
            $value = $property->formatValueForDatabase($value);
250
251
            $array[":$column"] = $value;
252
        }
253
254
        return $array;
255
    }
256
}
257