ConstraintToParamsDocTransformer   A
last analyzed

Complexity

Total Complexity 37

Size/Duplication

Total Lines 264
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 97
c 3
b 0
f 0
dl 0
loc 264
ccs 92
cts 92
cp 1
rs 9.44
wmc 37

13 Methods

Rating   Name   Duplication   Size   Complexity  
A addToAllowedValueListIfNotExist() 0 4 2
A transformList() 0 9 2
A appendAllConstraintToDoc() 0 8 2
A addListToAllowedValueListIfNotExist() 0 4 2
A transform() 0 3 1
A appendToDoc() 0 15 6
A basicAppendToDoc() 0 12 3
A appendExistenceConstraintData() 0 5 2
A __construct() 0 10 1
A appendChoiceAllowedValue() 0 8 3
A appendCollectionDoc() 0 17 4
A setNulNotNullConstraintData() 0 18 5
A appendValidItemListDoc() 0 17 4
1
<?php
2
namespace Yoanm\JsonRpcParamsSymfonyConstraintDoc\Infra\Transformer;
3
4
use Symfony\Component\Validator\Constraint;
5
use Symfony\Component\Validator\Constraints as Assert;
6
use Yoanm\JsonRpcParamsSymfonyConstraintDoc\App\Helper\ClassComparatorTrait;
7
use Yoanm\JsonRpcParamsSymfonyConstraintDoc\App\Helper\ConstraintPayloadDocHelper;
8
use Yoanm\JsonRpcParamsSymfonyConstraintDoc\App\Helper\DocTypeHelper;
9
use Yoanm\JsonRpcParamsSymfonyConstraintDoc\App\Helper\MinMaxHelper;
10
use Yoanm\JsonRpcParamsSymfonyConstraintDoc\App\Helper\StringDocHelper;
11
use Yoanm\JsonRpcServerDoc\Domain\Model\Type\ArrayDoc;
12
use Yoanm\JsonRpcServerDoc\Domain\Model\Type\CollectionDoc;
13
use Yoanm\JsonRpcServerDoc\Domain\Model\Type\TypeDoc;
14
15
/**
16
 * Class ConstraintToParamsDocTransformer
17
 */
18
class ConstraintToParamsDocTransformer
19
{
20
    use ClassComparatorTrait;
21
22
    /** @var DocTypeHelper */
23
    private $docTypeHelper;
24
    /** @var StringDocHelper */
25
    private $stringDocHelper;
26
    /** @var MinMaxHelper */
27
    private $minMaxHelper;
28
    /** @var ConstraintPayloadDocHelper */
29
    private $constraintPayloadDocHelper;
30
31
    const CONSTRAINT_WITH_ALLOWED_VALUE_LIST = [
32
        Assert\IsTrue::class => [true, 1, '1'],
33
        Assert\IsFalse::class => [false, 0, '0'],
34
        Assert\IsNull::class => [null],
35
    ];
36
37
    const CONSTRAINT_WITH_ALLOWED_VALUE_LIST_FROM_PROPERTY = [
38
        Assert\IdenticalTo::class => 'value',
39
        Assert\EqualTo::class => 'value',
40
    ];
41
42
    const NULL_NOT_NULL_CONSTRAINT_LIST = [
43
        Assert\NotNull::class,
44
        Assert\IsTrue::class, // If it is true, it cannot be null ...
45
        Assert\IsFalse::class, // If it is false, it cannot be null ...
46
        // If should be identical to something, it cannot be null (but can be identical to null)
47
        Assert\IdenticalTo::class,
48
    ];
49
50
51
52
    /**
53
     * @param DocTypeHelper              $docTypeHelper
54
     * @param StringDocHelper            $stringDocHelper
55
     * @param MinMaxHelper               $minMaxHelper
56
     * @param ConstraintPayloadDocHelper $constraintPayloadDocHelper
57
     */
58 171
    public function __construct(
59
        DocTypeHelper $docTypeHelper,
60
        StringDocHelper $stringDocHelper,
61
        MinMaxHelper $minMaxHelper,
62
        ConstraintPayloadDocHelper $constraintPayloadDocHelper
63
    ) {
64 171
        $this->docTypeHelper = $docTypeHelper;
65 171
        $this->minMaxHelper = $minMaxHelper;
66 171
        $this->stringDocHelper = $stringDocHelper;
67 171
        $this->constraintPayloadDocHelper = $constraintPayloadDocHelper;
68
    }
69
70
    /**
71
     * @param Constraint $constraint
72
     *
73
     * @return TypeDoc
74
     *
75
     * @throws \ReflectionException
76
     */
77 170
    public function transform(Constraint $constraint) : TypeDoc
78
    {
79 170
        return $this->transformList([$constraint]);
80
    }
81
82
    /**
83
     * @param Constraint[] $constraintList
84
     *
85
     * @return TypeDoc
86
     *
87
     * @throws \ReflectionException
88
     */
89 171
    public function transformList(array $constraintList) : TypeDoc
90
    {
91 171
        $constraintDoc = $this->docTypeHelper->guess($constraintList);
92
93 171
        foreach ($constraintList as $constraint) {
94 171
            $this->appendToDoc($constraintDoc, $constraint);
95
        }
96
97 171
        return $constraintDoc;
98
    }
99
100
    /**
101
     * @param TypeDoc    $doc
102
     * @param Constraint $constraint
103
     *
104
     * @throws \ReflectionException
105
     */
106 171
    private function appendToDoc(TypeDoc $doc, Constraint $constraint) : void
107
    {
108 171
        if ($constraint instanceof Assert\Callback) {
109 3
            $callbackResult = call_user_func($constraint->callback);
110 3
            $callbackResultList = is_array($callbackResult) ? $callbackResult : [$callbackResult];
111 3
            foreach ($callbackResultList as $subConstraint) {
112 3
                $this->appendToDoc($doc, $subConstraint);
113
            }
114 171
        } elseif ($doc instanceof ArrayDoc && $constraint instanceof Assert\All) {
115 4
            $this->appendAllConstraintToDoc($doc, $constraint);
116
        } else {
117 171
            $this->basicAppendToDoc($doc, $constraint);
118
        }
119
        // /!\ Payload doc will override values even if already defined
120 171
        $this->constraintPayloadDocHelper->appendPayloadDoc($doc, $constraint);
121
    }
122
123
    /**
124
     * @param TypeDoc    $doc
125
     * @param Constraint $constraint
126
     *
127
     * @throws \ReflectionException
128
     */
129 171
    private function appendCollectionDoc(TypeDoc $doc, Constraint $constraint) : void
130
    {
131
        // If not a collection => give up
132 171
        if (!$doc instanceof CollectionDoc) {
133 152
            return;
134
        }
135
136 26
        if ($constraint instanceof Assert\Collection) {
137 8
            foreach ($constraint->fields as $fieldName => $subConstraint) {
138 7
                $sibling = $this->transform($subConstraint);
139 7
                $doc->addSibling(
140 7
                    $sibling->setName($fieldName)
141 7
                );
142
            }
143
144 8
            $doc->setAllowExtraSibling($constraint->allowExtraFields === true);
145 8
            $doc->setAllowMissingSibling($constraint->allowMissingFields === true);
146
        }
147
    }
148
149
    /**
150
     * @param TypeDoc $doc
151
     * @param Constraint $constraint
152
     */
153 171
    private function appendValidItemListDoc(TypeDoc $doc, Constraint $constraint) : void
154
    {
155 171
        if ($constraint instanceof Assert\Choice) {
156 6
            $this->appendChoiceAllowedValue($doc, $constraint);
157 165
        } elseif (null !== ($match = $this->getMatchingClassNameIn(
158 165
            $constraint,
159 165
            array_keys(self::CONSTRAINT_WITH_ALLOWED_VALUE_LIST_FROM_PROPERTY)
160 165
        ))) {
161 13
            $this->addToAllowedValueListIfNotExist(
162 13
                $doc,
163 13
                $constraint->{self::CONSTRAINT_WITH_ALLOWED_VALUE_LIST_FROM_PROPERTY[$match]}
164 13
            );
165 152
        } elseif (null !== ($match = $this->getMatchingClassNameIn(
166 152
            $constraint,
167 152
            array_keys(self::CONSTRAINT_WITH_ALLOWED_VALUE_LIST)
168 152
        ))) {
169 10
            $this->addListToAllowedValueListIfNotExist($doc, self::CONSTRAINT_WITH_ALLOWED_VALUE_LIST[$match]);
170
        }
171
    }
172
173
    /**
174
     * @param ArrayDoc   $doc
175
     * @param Assert\All $constraint
176
     *
177
     * @throws \ReflectionException
178
     */
179 4
    private function appendAllConstraintToDoc(ArrayDoc $doc, Assert\All $constraint) : void
180
    {
181 4
        $itemDoc = $this->docTypeHelper->guess($constraint->constraints);
182 4
        foreach ($constraint->constraints as $subConstraint) {
183 4
            $this->appendToDoc($itemDoc, $subConstraint);
184
        }
185
186 4
        $doc->setItemValidation($itemDoc);
187
    }
188
189
    /**
190
     * @param TypeDoc $doc
191
     * @param mixed[] $valueList
192
     */
193 16
    private function addListToAllowedValueListIfNotExist(TypeDoc $doc, array $valueList) : void
194
    {
195 16
        foreach ($valueList as $value) {
196 16
            $this->addToAllowedValueListIfNotExist($doc, $value);
197
        }
198
    }
199
200
    /**
201
     * @param TypeDoc $doc
202
     * @param mixed   $value
203
     */
204 29
    private function addToAllowedValueListIfNotExist(TypeDoc $doc, $value) : void
205
    {
206 29
        if (!in_array($value, $doc->getAllowedValueList(), true)) {
207 29
            $doc->addAllowedValue($value);
208
        }
209
    }
210
211
    /**
212
     * @param TypeDoc    $doc
213
     * @param Constraint $constraint
214
     *
215
     * @throws \ReflectionException
216
     */
217 171
    private function basicAppendToDoc(TypeDoc $doc, Constraint $constraint): void
218
    {
219 171
        $this->stringDocHelper->append($doc, $constraint);
220 171
        $this->appendCollectionDoc($doc, $constraint);
221
222 171
        $this->minMaxHelper->append($doc, $constraint);
223 171
        $this->appendValidItemListDoc($doc, $constraint);
224
225 171
        if ($constraint instanceof Assert\Existence) {
226 9
            $this->appendExistenceConstraintData($doc, $constraint);
227 171
        } elseif (null !== ($match = $this->getMatchingClassNameIn($constraint, self::NULL_NOT_NULL_CONSTRAINT_LIST))) {
228 25
            $this->setNulNotNullConstraintData($doc, $constraint, $match);
229
        }
230
    }
231
232
    /**
233
     * @param TypeDoc       $doc
234
     * @param Assert\Choice $constraint
235
     */
236 6
    private function appendChoiceAllowedValue(TypeDoc $doc, Assert\Choice $constraint): void
237
    {
238 6
        if ($constraint->callback && is_callable($constraint->callback)) {
239 1
            $choiceList = call_user_func($constraint->callback);
240
        } else {
241 5
            $choiceList = $constraint->choices ?? [];
242
        }
243 6
        $this->addListToAllowedValueListIfNotExist($doc, $choiceList);
244
    }
245
246
    /**
247
     * @param TypeDoc    $doc
248
     * @param Constraint $constraint
249
     * @param string     $sanitizedClass
250
     */
251 25
    private function setNulNotNullConstraintData(TypeDoc $doc, Constraint $constraint, string $sanitizedClass): void
252
    {
253 25
        $isIdenticalTo = $sanitizedClass === Assert\IdenticalTo::class;
254 25
        $doc->setNullable($isIdenticalTo ? is_null($constraint->value) : false);
255 25
        $defaultValue = $exampleValue = null;
256
        switch (true) {
257
            case $sanitizedClass === Assert\IsTrue::class:
258 4
                $defaultValue = $exampleValue = true;
259 4
                break;
260 21
            case $sanitizedClass === Assert\IsFalse::class:
261 4
                $defaultValue = $exampleValue = false;
262 4
                break;
263 17
            case $isIdenticalTo:
264 10
                $defaultValue = $exampleValue = $constraint->value;
265 10
                break;
266
        }
267 25
        $doc->setDefault($doc->getDefault() ?? $defaultValue);
268 25
        $doc->setExample($doc->getExample() ?? $exampleValue);
269
    }
270
271
    /**
272
     * @param TypeDoc          $doc
273
     * @param Assert\Existence $constraint
274
     *
275
     * @throws \ReflectionException
276
     */
277 9
    private function appendExistenceConstraintData(TypeDoc $doc, Assert\Existence $constraint): void
278
    {
279 9
        $doc->setRequired($constraint instanceof Assert\Required);
280 9
        foreach ($constraint->constraints as $subConstraint) {
281 9
            $this->appendToDoc($doc, $subConstraint);
282
        }
283
    }
284
}
285