Completed
Push — feature/controller ( 5a6415...9e7a26 )
by René
07:43 queued 05:40
created

Entity::isAltered()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
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|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 2
    public function set(string $key, $value)
73
    {
74 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...
75 1
            throw new InvalidEntityPropertyException([get_class($this), $key]);
76
        }
77
78 2
        $newValue = $this->validatePropertyValueType($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|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 2
    public function get(string $key)
103
    {
104 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...
105 1
            throw new InvalidEntityPropertyException([get_class($this), $key]);
106
        }
107
108 1
        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 3
    public function validatePropertyValueType(string $key, $value)
123
    {
124 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...
125 1
            throw new InvalidEntityPropertyException([get_class($this), $key]);
126
        }
127
128
        /**
129
         * Allow NULL
130
         */
131 3
        if ($value !== null) {
132 3
            $valueType = strtolower(gettype($value));
133
134
            /**
135
             * Get class if object
136
             */
137 3
            if ($valueType === 'object') {
138 3
                $valueType = strtolower(get_class($value));
139
            }
140
141
            /**
142
             * Handle alias types
143
             */
144 3
            $columnType = self::getColumns()[$key];
145
146
            switch ($columnType) {
147 3
                case 'date':
148 3
                    $columnType = 'datetime';
149 3
                    break;
150
            }
151
152
            /**
153
             * Validate type
154
             */
155 3
            if ($valueType !== $columnType) {
156 1
                throw new InvalidValueTypeForEntityPropertyException([
157 1
                    get_class($this),
158 1
                    $key,
159 1
                    $columnType,
160 1
                    $valueType
161
                ]);
162
            }
163
        }
164
165 3
        return $value;
166
    }
167
168
    /**
169
     * Check if entity has been altered
170
     *
171
     * @return bool True if altered, otherwise false
172
     */
173 1
    public function isAltered(): bool
174
    {
175 1
        return count($this->alteredColumns) > 0;
176
    }
177
178
    /**
179
     * Marks the entity as unaltered
180
     */
181 1
    public function setUnaltered()
182
    {
183 1
        $this->alteredColumns = [];
184 1
    }
185
186
    /**
187
     * Get altered columns
188
     *
189
     * @return array
190
     */
191 1
    public function getAlteredColumns(): array
192
    {
193 1
        return $this->alteredColumns;
194
    }
195
196
    /**
197
     * Return table structur for saving
198
     * Example: `[':{table_field_name}' => $this->fieldName]`
199
     *
200
     * @param bool $includeId Should the ID column be included
201
     *
202
     * @return array
203
     */
204 2
    public function toArray(bool $includeId): array
205
    {
206 2
        $columns = self::getColumns();
207
208 2
        if (!$includeId) {
209 1
            unset($columns['id']);
210
        }
211
212 2
        return $this->toArrayFromColumns($columns);
213
    }
214
215 2
    public function alteredToArray(bool $includeId): array
216
    {
217 2
        $alteredColumns = $this->alteredColumns;
218
219 2
        if ($includeId) {
220 1
            $alteredColumns['id'] = true;
221
        }
222
223 2
        return $this->toArrayFromColumns(array_intersect_key(self::getColumns(), $alteredColumns));
224
    }
225
226
    protected function toArrayFromColumns(array $columns): array
227
    {
228
        $array = [];
229
230
        foreach ($columns as $column => $type) {
231
            $property = new EntityProperty($type);
232
233
            $value = $this->get($column);
234
            $value = $property->formatValueForDatabase($value);
235
236
            $array[":$column"] = $value;
237
        }
238
239
        return $array;
240
    }
241
}
242