Completed
Push — feature/controller ( a5988d...b3a825 )
by René
03:15
created

Entity::alteredToArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2
Metric Value
dl 0
loc 10
ccs 5
cts 5
cp 1
rs 9.4285
cc 2
eloc 5
nc 2
nop 1
crap 2
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 1
    public static function getColumns(): array
50
    {
51 1
        $columns = array_merge([
52
            'id' => 'integer'
53 1
        ], static::$columns);
54
55 1
        $columns = array_merge($columns, [
56 1
            'modified' => 'datetime',
57
            'created'  => 'datetime'
58
        ]);
59
60 1
        return $columns;
61
    }
62
63
    /**
64
     * Set entity property
65
     *
66
     * @param string                                          $key   Entity property name
67
     * @param object|integer|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 2
    public function set(string $key, $value)
73
    {
74 2
        if (!isset(self::getColumns()[$key])) {
75 1
            throw new InvalidEntityPropertyException([get_class($this), $key]);
76
        }
77
78 2
        $newValue = $this->validatePropertyForValue($key, $value);
79
80 2
        if (!isset($this->properties[$key]) || $this->properties[$key] !== $newValue) {
81
            /**
82
             * Set internal property
83
             */
84 2
            $this->properties[$key] = $newValue;
85
86
            /**
87
             * Set altered column
88
             */
89 2
            $this->alteredColumns[$key] = true;
90
        }
91 2
    }
92
93
    /**
94
     * Get entity property
95
     *
96
     * @param string $key Entity property
97
     *
98
     * @return object|integer|double|string|array|boolean|null Entity property value for given key
99
     *
100
     * @throws InvalidEntityPropertyException If entity does not have that property
101
     */
102 2
    public function get(string $key)
103
    {
104 2
        if (!isset(self::getColumns()[$key])) {
105 1
            throw new InvalidEntityPropertyException([get_class($this), $key]);
106
        }
107
108 1
        return $this->properties[$key];
109
    }
110
111
    /**
112
     * Check if entity has been altered
113
     *
114
     * @return bool True if altered, otherwise false
115
     */
116 1
    public function isAltered(): bool
117
    {
118 1
        return count($this->alteredColumns) > 0;
119
    }
120
121
    /**
122
     * Marks the entity as unaltered
123
     */
124 1
    public function setUnaltered()
125
    {
126 1
        $this->alteredColumns = [];
127 1
    }
128
129
    /**
130
     * Get altered columns
131
     *
132
     * @return array
133
     */
134 1
    public function getAlteredColumns(): array
135
    {
136 1
        return $this->alteredColumns;
137
    }
138
139
    /**
140
     * Return table structur for saving
141
     * Example: `[':{table_field_name}' => $this->fieldName]`
142
     *
143
     * @param bool $includeId Should the ID column be included
144
     *
145
     * @return array
146
     */
147 2
    public function toArray(bool $includeId): array
148
    {
149 2
        $columns = self::getColumns();
150
151 2
        if (!$includeId) {
152 1
            unset($columns['id']);
153
        }
154
155 2
        return $this->toArrayFromColumns($columns);
156
    }
157
158 2
    public function alteredToArray(bool $includeId): array
159
    {
160 2
        $alteredColumns = $this->alteredColumns;
161
162 2
        if ($includeId) {
163 1
            $alteredColumns['id'] = true;
164
        }
165
166 2
        return $this->toArrayFromColumns(array_intersect_key(self::getColumns(), $alteredColumns));
167
    }
168
169
    protected function toArrayFromColumns(array $columns): array
170
    {
171
        $array = [];
172
173
        foreach ($columns as $column => $type) {
174
            $property = new EntityProperty($type);
175
176
            $value = $this->get($column);
177
            $value = $property->formatValueForDatabase($value);
178
179
            $array[":$column"] = $value;
180
        }
181
182
        return $array;
183
    }
184
185
    /**
186
     * Validate property for given value
187
     *
188
     * @param string $key   Entity property name
189
     * @param mixed  $value Entity property value
190
     *
191
     * @return object|integer|double|string|array|boolean|null Value
192
     *
193
     * @throws InvalidEntityPropertyException If entity does not have that property
194
     * @throws InvalidValueTypeForEntityPropertyException If value is of the wrong type
195
     */
196 3
    protected function validatePropertyForValue(string $key, $value)
197
    {
198 3
        if (!isset(self::getColumns()[$key])) {
199 1
            throw new InvalidEntityPropertyException([get_class($this), $key]);
200
        }
201
202
        /**
203
         * Allow NULL
204
         */
205 3
        if ($value !== null) {
206 3
            $valueType = strtolower(gettype($value));
207
208
            /**
209
             * Get class if object
210
             */
211 3
            if ($valueType === 'object') {
212 3
                $valueType = strtolower(get_class($value));
213
            }
214
215
            /**
216
             * Handle alias types
217
             */
218 3
            $columnType = self::getColumns()[$key];
219
220
            switch ($columnType) {
221 3
                case 'date':
222 3
                    $columnType = 'datetime';
223 3
                    break;
224
            }
225
226
            /**
227
             * Validate type
228
             */
229 3
            if ($valueType !== $columnType) {
230 1
                throw new InvalidValueTypeForEntityPropertyException([
231 1
                    get_class($this),
232 1
                    $key,
233 1
                    $valueType,
234 1
                    $columnType
235
                ]);
236
            }
237
        }
238
239 3
        return $value;
240
    }
241
}
242