Passed
Push — master ( 12c5ee...61343d )
by Herberto
01:56
created

DataMapper::mapPropertyValueToColumnValue()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 22
Code Lines 12

Duplication

Lines 14
Ratio 63.64 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 14
loc 22
ccs 0
cts 16
cp 0
rs 8.9197
c 0
b 0
f 0
cc 4
eloc 12
nc 4
nop 2
crap 20
1
<?php
2
3
namespace Hgraca\MicroOrm\DataMapper;
4
5
use DateTime;
6
use Hgraca\Common\Collection\Contract\CollectionInterface;
7
use Hgraca\Common\Entity\Concept\EntityAbstract;
8
use Hgraca\Common\Entity\Contract\EntityInterface;
9
use Hgraca\Helper\ClassHelper;
10
use Hgraca\MicroOrm\RepositoryInterface;
11
use ReflectionProperty;
12
13
class DataMapper implements DataMapperInterface
14
{
15
    /** @var EntityAbstract|string */
16
    private $entityFqcn;
17
18
    /** @var CollectionInterface|string */
19
    private $collectionFqcn;
20
21
    /** @var string */
22
    private $tableName;
23
24
    /** @var RepositoryInterface|string */
25
    private $repositoryFqcn;
26
27
    /** @var array */
28
    private $attributes;
29
30
    /** @var array */
31
    private $propertyToColumnMapper;
32
33
    /** @var array */
34
    private $propertyTypeMapper;
35
36
    /** @var ReflectionProperty[] */
37
    private $reflectionPropertyList;
38
39
    /** @var string */
40
    private $dateTimeFormat;
41
42
    public function __construct(
43
        string $entityFqcn,
44
        array $config
45
    ) {
46
        $this->entityFqcn = $entityFqcn;
47
        $this->repositoryFqcn = $config['repositoryFqcn'];
48
        $this->collectionFqcn = $config['collectionFqcn'];
49
        $this->dateTimeFormat = $config['dateTimeFormat'];
50
        $this->tableName = $config['tableName'];
51
        $this->attributes = $config['attributes'];
52
    }
53
54
    private function getEntityFqcn(): string
55
    {
56
        return $this->entityFqcn;
57
    }
58
59
    private function getCollectionFqcn(): string
60
    {
61
        return $this->collectionFqcn;
62
    }
63
64
    public function getTableName(): string
65
    {
66
        return $this->tableName;
67
    }
68
69
    /**
70
     * @return RepositoryInterface|string
71
     */
72
    public function getRepositoryFqcn(): string
73
    {
74
        return $this->repositoryFqcn;
75
    }
76
77
    public function mapRecordListToEntityCollection(array $recordList): CollectionInterface
78
    {
79
        $entityResultList = [];
80
        foreach ($recordList as $record) {
81
            $entityResultList[] = $this->mapRecordToEntity($record);
82
        }
83
84
        $collectionFqcn = $this->getCollectionFqcn();
85
86
        return new $collectionFqcn($entityResultList);
87
    }
88
89
    public function mapEntityToRecord(EntityInterface $entity): array
90
    {
91
        $entityFqcn = $this->getEntityFqcn();
92
        $reflectionPropertyList = $this->getReflectionPropertyList($entityFqcn);
93
94
        $record = [];
95
        foreach ($reflectionPropertyList as $reflectionProperty) {
96
            $propertyName = $reflectionProperty->getName();
97
            $propertyValue = $reflectionProperty->getValue($entity);
98
99
            $record += $this->mapPropertyToColumn($propertyName, $propertyValue);
100
        }
101
102
        return $record;
103
    }
104
105
    public function mapPropertiesToColumns(array $propertyList): array
106
    {
107
        $columnList = [];
108
        foreach ($propertyList as $propertyName => $propertyValue) {
109
            $columnList += $this->mapPropertyToColumn($propertyName, $propertyValue);
110
        }
111
112
        return $columnList;
113
    }
114
115
    public function mapRecordToEntity(array $record): EntityInterface
116
    {
117
        $entity = $this->createEntity();
118
119
        $this->updateEntityFromRecord($entity, $record);
120
121
        return $entity;
122
    }
123
124
    public function mapColumnsToProperties(array $columnList): array
125
    {
126
        $propertyList = [];
127
128
        foreach ($columnList as $columnName => $columnValue) {
129
            $propertyList += $this->mapColumnToProperty($columnName, $columnValue);
130
        }
131
132
        return $propertyList;
133
    }
134
135
    public function updateEntityFromRecord(EntityInterface $entity, array $record)
136
    {
137
        $reflectionPropertyList = $this->getReflectionPropertyList($this->getEntityFqcn());
138
        $newPropertyValueArray = $this->mapColumnsToProperties($record);
139
140
        foreach ($reflectionPropertyList as $reflectionProperty) {
141
            $propertyName = $reflectionProperty->getName();
142
143
            if (!isset($newPropertyValueArray[$propertyName])) {
144
                continue;
145
            }
146
147
            $reflectionProperty->setValue($entity, $newPropertyValueArray[$propertyName]);
148
        }
149
    }
150
151 View Code Duplication
    public function getPropertyToColumnMapper(): array
152
    {
153
        if (null !== $this->propertyToColumnMapper) {
154
            return $this->propertyToColumnMapper;
155
        }
156
157
        $this->propertyToColumnMapper = [];
158
        foreach ($this->attributes as $attributeName => $attributeMetadata) {
159
            $this->propertyToColumnMapper[$attributeName] = $attributeMetadata['column'] ?? $attributeName;
160
        }
161
162
        return $this->propertyToColumnMapper;
163
    }
164
165
    /**
166
     * @param string $entityFqcn
167
     *
168
     * @return ReflectionProperty[]
169
     */
170
    private function getReflectionPropertyList(string $entityFqcn): array
171
    {
172
        if (null !== $this->reflectionPropertyList) {
173
            return $this->reflectionPropertyList;
174
        }
175
176
        $this->reflectionPropertyList = ClassHelper::getReflectionProperties($entityFqcn);
177
        ClassHelper::setReflectionPropertiesAccessible($this->reflectionPropertyList);
178
179
        return $this->reflectionPropertyList;
180
    }
181
182
    private function mapColumnValueToPropertyValue($value, string $type)
183
    {
184
        // TODO use a serializer service for this, so we can have ValueObjects
185 View Code Duplication
        switch ($type) {
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...
186
            case 'datetime':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
187
                return DateTime::createFromFormat($this->dateTimeFormat, $value);
188
189
            case 'text':
190
                settype($value, 'string');
191
192
                return $value;
193
194
            default:
195
                settype($value, $type);
196
197
                return $value;
198
        }
199
    }
200
201
    private function mapPropertyValueToColumnValue($value, string $type)
202
    {
203
        if (null === $value) {
204
            return $value;
205
        }
206
207
        // TODO use a serializer service for this, so we can have ValueObjects
208 View Code Duplication
        switch ($type) {
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...
209
            case 'datetime':
210
                return date_format($value, $this->dateTimeFormat);
211
212
            case 'text':
213
                settype($value, 'string');
214
215
                return $value;
216
217
            default:
218
                settype($value, $type);
219
220
                return $value;
221
        }
222
    }
223
224
    private function createEntity(): EntityInterface
225
    {
226
        $entityFqcn = $this->getEntityFqcn();
227
228
        return new $entityFqcn();
229
    }
230
231
    private function mapPropertyToColumn(string $propertyName, $propertyValue): array
232
    {
233
        $propertyType = $this->getPropertyType($propertyName);
234
        $columnName = $this->getColumnName($propertyName);
235
236
        return [$columnName => $this->mapPropertyValueToColumnValue($propertyValue, $propertyType)];
237
    }
238
239
    private function mapColumnToProperty(string $columnName, $columnValue): array
240
    {
241
        $propertyName = $this->getPropertyName($columnName);
242
        $propertyType = $this->getPropertyType($propertyName);
243
244
        return [$propertyName => $this->mapColumnValueToPropertyValue($columnValue, $propertyType)];
245
    }
246
247
    private function getColumnToPropertyMapper(): array
248
    {
249
        return array_flip($this->getPropertyToColumnMapper());
250
    }
251
252 View Code Duplication
    private function getPropertyTypeMapper(): array
253
    {
254
        if (null !== $this->propertyTypeMapper) {
255
            return $this->propertyTypeMapper;
256
        }
257
258
        $this->propertyTypeMapper = [];
259
        foreach ($this->attributes as $attributeName => $attributeMetadata) {
260
            $this->propertyTypeMapper[$attributeName] = $attributeMetadata['type'] ?? 'string';
261
        }
262
263
        return $this->propertyTypeMapper;
264
    }
265
266
    private function getPropertyType($propertyName): string
267
    {
268
        $propertyTypeMapper = $this->getPropertyTypeMapper();
269
270
        return $propertyTypeMapper[$propertyName] ?? 'string';
271
    }
272
273
    private function getColumnName(string $propertyName): string
274
    {
275
        $propertyToColumnMapper = $this->getPropertyToColumnMapper();
276
277
        return $propertyToColumnMapper[$propertyName] ?? $propertyName;
278
    }
279
280
    private function getPropertyName(string $columnName): string
281
    {
282
        $columnToPropertyMapper = $this->getColumnToPropertyMapper();
283
284
        return $columnToPropertyMapper[$columnName] ?? $columnName;
285
    }
286
}
287