ChoiceMapping   A
last analyzed

Complexity

Total Complexity 27

Size/Duplication

Total Lines 218
Duplicated Lines 0 %

Test Coverage

Coverage 87.32%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 84
c 1
b 0
f 1
dl 0
loc 218
ccs 62
cts 71
cp 0.8732
rs 10
wmc 27

11 Methods

Rating   Name   Duplication   Size   Complexity  
A resolveValue() 0 34 5
A assertValue() 0 28 5
A getChoices() 0 3 1
A wakeUpMapping() 0 6 2
A describeOrigin() 0 3 1
A getDeterminatorColumn() 0 3 1
A getDeterminatorColumnName() 0 3 1
A addChoice() 0 3 1
A __construct() 0 24 3
A collectDBALColumns() 0 17 2
A revertValue() 0 49 5
1
<?php
2
/**
3
 * Copyright (C) 2018 Gerrit Addiks.
4
 * This package (including this file) was released under the terms of the GPL-3.0.
5
 * You should have received a copy of the GNU General Public License along with this program.
6
 * If not, see <http://www.gnu.org/licenses/> or send me a mail so i can send you a copy.
7
 * @license GPL-3.0
8
 * @author Gerrit Addiks <[email protected]>
9
 */
10
11
namespace Addiks\RDMBundle\Mapping;
12
13
use Addiks\RDMBundle\Mapping\MappingInterface;
14
use Doctrine\DBAL\Schema\Column;
15
use Doctrine\DBAL\Types\Type;
16
use Addiks\RDMBundle\Exception\InvalidMappingException;
17
use Addiks\RDMBundle\Hydration\HydrationContextInterface;
18
use Symfony\Component\DependencyInjection\ContainerInterface;
19
use Exception;
20
use Webmozart\Assert\Assert;
21
use Doctrine\ORM\Exception\ORMException;
22
23
final class ChoiceMapping implements MappingInterface
24
{
25
26
    /**
27
     * @var Column
28
     */
29
    private $determinatorColumn;
30
31
    /**
32
     * @var array<MappingInterface>
33
     */
34
    private $choiceMappings = array();
35
36
    /**
37
     * @var string
38
     */
39
    private $originDescription;
40
41
    /**
42
     * @param string|Column $determinatorColumn
43
     */
44 16
    public function __construct(
45
        $determinatorColumn,
46
        array $choiceMappings,
47
        string $originDescription = ""
48
    ) {
49 16
        if (!$determinatorColumn instanceof Column) {
50 6
            Assert::string($determinatorColumn);
51 6
            $determinatorColumn = new Column(
52
                $determinatorColumn,
53 6
                Type::getType('string'),
54
                [
55 6
                    'notnull' => false,
56
                    'length'  => 255,
57
                ]
58
            );
59
        }
60
61 16
        $this->determinatorColumn = clone $determinatorColumn;
62 16
        $this->originDescription = $originDescription;
63
64 16
        foreach ($choiceMappings as $determinator => $choiceMapping) {
65
            /** @var MappingInterface $choiceMapping */
66
67 15
            $this->addChoice($determinator, $choiceMapping);
68
        }
69
    }
70
71 1
    public function getChoices(): array
72
    {
73 1
        return $this->choiceMappings;
74
    }
75
76 1
    public function describeOrigin(): string
77
    {
78 1
        return $this->originDescription;
79
    }
80
81 4
    public function collectDBALColumns(): array
82
    {
83
        /** @var array<Column> $additionalMappings */
84 4
        $additionalMappings = array(
85 4
            clone $this->determinatorColumn
86
        );
87
88 4
        foreach ($this->choiceMappings as $choiceMapping) {
89
            /** @var MappingInterface $choiceMapping */
90
91 3
            $additionalMappings = array_merge(
92
                $additionalMappings,
93 3
                $choiceMapping->collectDBALColumns()
94
            );
95
        }
96
97 4
        return $additionalMappings;
98
    }
99
100 1
    public function getDeterminatorColumn(): Column
101
    {
102 1
        return clone $this->determinatorColumn;
103
    }
104
105 1
    public function getDeterminatorColumnName(): string
106
    {
107 1
        return $this->determinatorColumn->getName();
108
    }
109
110 15
    private function addChoice(string $determinator, MappingInterface $choiceMapping): void
111
    {
112 15
        $this->choiceMappings[$determinator] = $choiceMapping;
113
    }
114
115 2
    public function resolveValue(
116
        HydrationContextInterface $context,
117
        array $dataFromAdditionalColumns
118
    ) {
119
        /** @var mixed $value */
120 2
        $value = null;
121
122
        /** @var string $determinatorColumn */
123 2
        $determinatorColumn = $this->determinatorColumn->getName();
124
125 2
        if (array_key_exists($determinatorColumn, $dataFromAdditionalColumns)) {
126
            /** @var string|int $determinatorValue */
127 2
            $determinatorValue = $dataFromAdditionalColumns[$determinatorColumn];
128
129 2
            if (!empty($determinatorValue) && !array_key_exists($determinatorValue, $this->choiceMappings)) {
130
                throw new InvalidMappingException(sprintf(
131
                    "Invalid option-value '%s' for choice-column '%s' on entity %s!",
132
                    $determinatorValue,
133
                    $determinatorColumn,
134
                    $context->getEntityClass()
135
                ));
136
            }
137
138 2
            if (isset($this->choiceMappings[$determinatorValue])) {
139 2
                $choiceMapping = $this->choiceMappings[$determinatorValue];
140
141 2
                $value = $choiceMapping->resolveValue(
142
                    $context,
143
                    $dataFromAdditionalColumns
144
                );
145
            }
146
        }
147
148 2
        return $value;
149
    }
150
151 3
    public function revertValue(
152
        HydrationContextInterface $context,
153
        $valueFromEntityField
154
    ): array {
155
        /** @var array<string, scalar|null> $data */
156 3
        $data = array();
157
158
        /** @var string $determinatorColumn */
159 3
        $determinatorColumn = $this->determinatorColumn->getName();
160
161
        /** @var ?scalar $determinatorValue */
162 3
        $determinatorValue = null;
163
164 3
        foreach ($this->choiceMappings as $choiceDeterminatorValue => $choiceMapping) {
165
            /** @var MappingInterface $choiceMapping */
166
167 3
            $choiceValue = null;
168
169
            try {
170
                /** @var array<scalar> $choiceData */
171 3
                $choiceData = $choiceMapping->revertValue($context, $valueFromEntityField);
172
173 3
                $choiceValue = $choiceMapping->resolveValue(
174
                    $context,
175
                    $choiceData
176
                );
177
178 3
                if ($choiceValue === $valueFromEntityField) {
179 3
                    $determinatorValue = $choiceDeterminatorValue;
180 3
                    $data = $choiceData;
181 3
                    break;
182
                }
183
184
            } catch (ORMException $exception) {
185
                throw new ORMException(sprintf(
186
                    "For ORM-choice defined %s (option '%s'): %s",
187
                    $this->originDescription,
188
                    $choiceDeterminatorValue,
189
                    $exception->getMessage()
190
                ), 0, $exception);
191
192
            } catch (Exception $exception) {
193
                # This mapping did not match, continue with the next
194
            }
195
        }
196
197 3
        $data[$determinatorColumn] = $determinatorValue;
198
199 3
        return $data;
200
    }
201
202 2
    public function assertValue(
203
        HydrationContextInterface $context,
204
        array $dataFromAdditionalColumns,
205
        $actualValue
206
    ): void {
207
        /** @var string $determinatorColumn */
208 2
        $determinatorColumn = $this->determinatorColumn->getName();
209
210 2
        if (array_key_exists($determinatorColumn, $dataFromAdditionalColumns)) {
211
            /** @var string|int $determinatorValue */
212 2
            $determinatorValue = $dataFromAdditionalColumns[$determinatorColumn];
213
214 2
            if (!empty($determinatorValue) && !array_key_exists($determinatorValue, $this->choiceMappings)) {
215
                throw new InvalidMappingException(sprintf(
216
                    "Invalid option-value '%s' for choice-column '%s' on entity %s!",
217
                    $determinatorValue,
218
                    $determinatorColumn,
219
                    $context->getEntityClass()
220
                ));
221
            }
222
223 2
            if (isset($this->choiceMappings[$determinatorValue])) {
224 2
                $choiceMapping = $this->choiceMappings[$determinatorValue];
225
226 2
                $choiceMapping->assertValue(
227
                    $context,
228
                    $dataFromAdditionalColumns,
229
                    $actualValue
230
                );
231
            }
232
        }
233
    }
234
235 1
    public function wakeUpMapping(ContainerInterface $container): void
236
    {
237 1
        foreach ($this->choiceMappings as $choiceMapping) {
238
            /** @var MappingInterface $choiceMapping */
239
240 1
            $choiceMapping->wakeUpMapping($container);
241
        }
242
    }
243
244
}
245