Passed
Push — master ( d8fa97...555fd2 )
by Yo
01:32
created

addListToAllowedValueListIfNotExist()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 2
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 2
rs 10
c 0
b 0
f 0
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 18
    public function __construct(
59
        DocTypeHelper $docTypeHelper,
60
        StringDocHelper $stringDocHelper,
61
        MinMaxHelper $minMaxHelper,
62
        ConstraintPayloadDocHelper $constraintPayloadDocHelper
63
    ) {
64 18
        $this->docTypeHelper = $docTypeHelper;
65 18
        $this->minMaxHelper = $minMaxHelper;
66 18
        $this->stringDocHelper = $stringDocHelper;
67 18
        $this->constraintPayloadDocHelper = $constraintPayloadDocHelper;
68 18
    }
69
70
    /**
71
     * @param Constraint $constraint
72
     *
73
     * @return TypeDoc
74
     *
75
     * @throws \ReflectionException
76
     */
77 18
    public function transform(Constraint $constraint) : TypeDoc
78
    {
79 18
        return $this->transformList([$constraint]);
80
    }
81
82
    /**
83
     * @param Constraint[] $constraintList
84
     *
85
     * @return TypeDoc
86
     *
87
     * @throws \ReflectionException
88
     */
89 18
    public function transformList(array $constraintList) : TypeDoc
90
    {
91 18
        $constraintDoc = $this->docTypeHelper->guess($constraintList);
92
93 18
        foreach ($constraintList as $constraint) {
94 18
            $this->appendToDoc($constraintDoc, $constraint);
95
        }
96
97 18
        return $constraintDoc;
98
    }
99
100
    /**
101
     * @param TypeDoc    $doc
102
     * @param Constraint $constraint
103
     *
104
     * @throws \ReflectionException
105
     */
106 18
    private function appendToDoc(TypeDoc $doc, Constraint $constraint) : void
107
    {
108 18
        if ($constraint instanceof Assert\Callback) {
109 1
            $callbackResult = call_user_func($constraint->callback);
110 1
            $callbackResultList = is_array($callbackResult) ? $callbackResult : [$callbackResult];
111 1
            foreach ($callbackResultList as $subConstraint) {
112 1
                $this->appendToDoc($doc, $subConstraint);
113
            }
114 18
        } elseif ($doc instanceof ArrayDoc && $constraint instanceof Assert\All) {
115 1
            $this->appendAllConstraintToDoc($doc, $constraint);
116
        } else {
117 18
            $this->basicAppendToDoc($doc, $constraint);
118
        }
119
        // /!\ Payload doc will override values even if already defined
120 18
        $this->constraintPayloadDocHelper->appendPayloadDoc($doc, $constraint);
121 18
    }
122
123
    /**
124
     * @param TypeDoc    $doc
125
     * @param Constraint $constraint
126
     *
127
     * @throws \ReflectionException
128
     */
129 18
    private function appendCollectionDoc(TypeDoc $doc, Constraint $constraint) : void
130
    {
131
        // If not a collection => give up
132 18
        if (!$doc instanceof CollectionDoc) {
133 18
            return;
134
        }
135
136 3
        if ($constraint instanceof Assert\Collection) {
137 3
            foreach ($constraint->fields as $fieldName => $subConstraint) {
138 3
                $sibling = $this->transform($subConstraint);
139 3
                $doc->addSibling(
140 3
                    $sibling->setName($fieldName)
141
                );
142
            }
143
144 3
            $doc->setAllowExtraSibling($constraint->allowExtraFields === true);
145 3
            $doc->setAllowMissingSibling($constraint->allowMissingFields === true);
146
        }
147 3
    }
148
149
    /**
150
     * @param TypeDoc $doc
151
     * @param Constraint $constraint
152
     */
153 18
    private function appendValidItemListDoc(TypeDoc $doc, Constraint $constraint) : void
154
    {
155 18
        if ($constraint instanceof Assert\Choice) {
156 2
            $this->appendChoiceAllowedValue($doc, $constraint);
157 16
        } elseif (null !== ($match = $this->getMatchingClassNameIn(
158 16
            $constraint,
159 16
            array_keys(self::CONSTRAINT_WITH_ALLOWED_VALUE_LIST_FROM_PROPERTY)
160
        ))) {
161 3
            $this->addToAllowedValueListIfNotExist(
162 3
                $doc,
163 3
                $constraint->{self::CONSTRAINT_WITH_ALLOWED_VALUE_LIST_FROM_PROPERTY[$match]}
164
            );
165 13
        } elseif (null !== ($match = $this->getMatchingClassNameIn(
166 13
            $constraint,
167 13
            array_keys(self::CONSTRAINT_WITH_ALLOWED_VALUE_LIST)
168
        ))) {
169 5
            $this->addListToAllowedValueListIfNotExist($doc, self::CONSTRAINT_WITH_ALLOWED_VALUE_LIST[$match]);
170
        }
171 18
    }
172
173
    /**
174
     * @param ArrayDoc   $doc
175
     * @param Assert\All $constraint
176
     *
177
     * @throws \ReflectionException
178
     */
179 1
    private function appendAllConstraintToDoc(ArrayDoc $doc, Assert\All $constraint) : void
180
    {
181 1
        $itemDoc = $this->docTypeHelper->guess($constraint->constraints);
182 1
        foreach ($constraint->constraints as $subConstraint) {
183 1
            $this->appendToDoc($itemDoc, $subConstraint);
184
        }
185
186 1
        $doc->setItemValidation($itemDoc);
187 1
    }
188
189
    /**
190
     * @param TypeDoc $doc
191
     * @param mixed[] $valueList
192
     */
193 7
    private function addListToAllowedValueListIfNotExist(TypeDoc $doc, array $valueList) : void
194
    {
195 7
        foreach ($valueList as $value) {
196 7
            $this->addToAllowedValueListIfNotExist($doc, $value);
197
        }
198 7
    }
199
200
    /**
201
     * @param TypeDoc $doc
202
     * @param mixed   $value
203
     */
204 10
    private function addToAllowedValueListIfNotExist(TypeDoc $doc, $value) : void
205
    {
206 10
        if (!in_array($value, $doc->getAllowedValueList(), true)) {
207 10
            $doc->addAllowedValue($value);
208
        }
209 10
    }
210
211
    /**
212
     * @param TypeDoc    $doc
213
     * @param Constraint $constraint
214
     *
215
     * @throws \ReflectionException
216
     */
217 18
    private function basicAppendToDoc(TypeDoc $doc, Constraint $constraint): void
218
    {
219 18
        $this->stringDocHelper->append($doc, $constraint);
220 18
        $this->appendCollectionDoc($doc, $constraint);
221
222 18
        $this->minMaxHelper->append($doc, $constraint);
223 18
        $this->appendValidItemListDoc($doc, $constraint);
224
225 18
        if ($constraint instanceof Assert\Existence) {
226 3
            $this->appendExistenceConstraintData($doc, $constraint);
227 18
        } elseif (null !== ($match = $this->getMatchingClassNameIn($constraint, self::NULL_NOT_NULL_CONSTRAINT_LIST))) {
228 10
            $this->setNulNotNullConstraintData($doc, $constraint, $match);
229
        }
230 18
    }
231
232
    /**
233
     * @param TypeDoc       $doc
234
     * @param Assert\Choice $constraint
235
     */
236 2
    private function appendChoiceAllowedValue(TypeDoc $doc, Assert\Choice $constraint): void
237
    {
238 2
        if ($constraint->callback && is_callable($constraint->callback)) {
239 1
            $choiceList = call_user_func($constraint->callback);
240
        } else {
241 1
            $choiceList = $constraint->choices ?? [];
242
        }
243 2
        $this->addListToAllowedValueListIfNotExist($doc, $choiceList);
244 2
    }
245
246
    /**
247
     * @param TypeDoc    $doc
248
     * @param Constraint $constraint
249
     * @param string     $sanitizedClass
250
     */
251 10
    private function setNulNotNullConstraintData(TypeDoc $doc, Constraint $constraint, string $sanitizedClass): void
252
    {
253 10
        $isIdenticalTo = $sanitizedClass === Assert\IdenticalTo::class;
254 10
        $doc->setNullable($isIdenticalTo ? is_null($constraint->value) : false);
255 10
        $defaultValue = $exampleValue = null;
256
        switch (true) {
257 10
            case $sanitizedClass === Assert\IsTrue::class:
258 2
                $defaultValue = $exampleValue = true;
259 2
                break;
260 8
            case $sanitizedClass === Assert\IsFalse::class:
261 2
                $defaultValue = $exampleValue = false;
262 2
                break;
263 6
            case $isIdenticalTo:
264 2
                $defaultValue = $exampleValue = $constraint->value;
265 2
                break;
266
        }
267 10
        $doc->setDefault($doc->getDefault() ?? $defaultValue);
268 10
        $doc->setExample($doc->getExample() ?? $exampleValue);
269 10
    }
270
271
    /**
272
     * @param TypeDoc          $doc
273
     * @param Assert\Existence $constraint
274
     *
275
     * @throws \ReflectionException
276
     */
277 3
    private function appendExistenceConstraintData(TypeDoc $doc, Assert\Existence $constraint): void
278
    {
279 3
        $doc->setRequired($constraint instanceof Assert\Required);
280 3
        foreach ($constraint->constraints as $subConstraint) {
281 3
            $this->appendToDoc($doc, $subConstraint);
282
        }
283 3
    }
284
}
285