Failed Conditions
Pull Request — release/1.0.0 (#7)
by Yo
02:47
created

appendCollectionDoc()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5.0144

Importance

Changes 0
Metric Value
cc 5
eloc 12
nc 5
nop 2
dl 0
loc 21
ccs 11
cts 12
cp 0.9167
crap 5.0144
rs 9.5555
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\ConstraintPayloadDocHelper;
7
use Yoanm\JsonRpcParamsSymfonyConstraintDoc\App\Helper\DocTypeHelper;
8
use Yoanm\JsonRpcParamsSymfonyConstraintDoc\App\Helper\MinMaxHelper;
9
use Yoanm\JsonRpcParamsSymfonyConstraintDoc\App\Helper\StringDocHelper;
10
use Yoanm\JsonRpcServerDoc\Domain\Model\Type\ArrayDoc;
11
use Yoanm\JsonRpcServerDoc\Domain\Model\Type\CollectionDoc;
12
use Yoanm\JsonRpcServerDoc\Domain\Model\Type\TypeDoc;
13
14
/**
15
 * Class ConstraintToParamsDocTransformer
16
 */
17
class ConstraintToParamsDocTransformer
18
{
19
    /** @var DocTypeHelper */
20
    private $docTypeHelper;
21
    /** @var StringDocHelper */
22
    private $stringDocHelper;
23
    /** @var MinMaxHelper */
24
    private $minMaxHelper;
25
    /** @var ConstraintPayloadDocHelper */
26
    private $constraintPayloadDocHelper;
27
28
    /**
29
     * @param DocTypeHelper              $docTypeHelper
30
     * @param ConstraintPayloadDocHelper $constraintPayloadDocHelper
31
     */
32 7
    public function __construct(
33
        DocTypeHelper $docTypeHelper,
34
        StringDocHelper $stringDocHelper,
35
        MinMaxHelper $minMaxHelper,
36
        ConstraintPayloadDocHelper $constraintPayloadDocHelper
37
    ) {
38 7
        $this->docTypeHelper = $docTypeHelper;
39 7
        $this->minMaxHelper = $minMaxHelper;
40 7
        $this->stringDocHelper = $stringDocHelper;
41 7
        $this->constraintPayloadDocHelper = $constraintPayloadDocHelper;
42 7
    }
43
44
    /**
45
     * @param Constraint $constraint
46
     *
47
     * @return TypeDoc
48
     */
49 7
    public function transform(Constraint $constraint) : TypeDoc
50
    {
51 7
        return $this->transformList([$constraint]);
52
    }
53
54
    /**
55
     * @param Constraint[] $constraintList
56
     *
57
     * @return TypeDoc
58
     */
59 7
    public function transformList(array $constraintList) : TypeDoc
60
    {
61 7
        $constraintDoc = $this->docTypeHelper->guess($constraintList);
62
63 7
        foreach ($constraintList as $constraint) {
64 7
            $this->appendToDoc($constraintDoc, $constraint);
65
        }
66
67 7
        return $constraintDoc;
68
    }
69
70
    /**
71
     * @param TypeDoc $doc
72
     * @param Constraint   $constraint
73
     */
74 7
    private function appendToDoc(TypeDoc $doc, Constraint $constraint) : void
75
    {
76 7
        static $notNullConstraintList = [
77
            Assert\NotNull::class,
78
            Assert\IsTrue::class, // If it is true, it cannot be null ...
79
            Assert\IsFalse::class, // If it is false, it cannot be null ...
80
            // If should be identical to something, it cannot be null (but can be identical to null)
81
            Assert\IdenticalTo::class,
82
        ];
83 7
        if ($constraint instanceof Assert\Callback) {
84
            $callbackResult = call_user_func($constraint->callback);
85
            $callbackResultList = is_array($callbackResult) ? $callbackResult : [$callbackResult];
86
            foreach ($callbackResultList as $subConstraint) {
87
                $this->appendToDoc($doc, $subConstraint);
88
            }
89 7
        } elseif ($doc instanceof ArrayDoc && $constraint instanceof Assert\All) {
90 1
            $this->appendAllConstraintToDoc($doc, $constraint);
91
        } else {
92 7
            $this->stringDocHelper->append($doc, $constraint);
93 7
            $this->appendCollectionDoc($doc, $constraint);
94
95 7
            $this->minMaxHelper->append($doc, $constraint);
96 7
            $this->appendValidItemListDoc($doc, $constraint);
97
98 7
            if ($constraint instanceof Assert\Existence) {
99 2
                $doc->setRequired($constraint instanceof Assert\Required);
100 2
                foreach ($constraint->constraints as $subConstraint) {
101 2
                    $this->appendToDoc($doc, $subConstraint);
102
                }
103 7
            } elseif ($this->isInstanceOfOneClassIn($constraint, $notNullConstraintList)) {
104 2
                $doc->setNullable(
105 2
                    ($constraint instanceof Assert\IdenticalTo)
106
                        ? is_null($constraint->value)
107 2
                        : false
108
                );
109 2
                $defaultValue = $exampleValue = null;
110
                switch (true) {
111 2
                    case $constraint instanceof Assert\IsTrue:
112
                        $defaultValue = $exampleValue = true;
113
                        break;
114 2
                    case $constraint instanceof Assert\IsFalse:
115
                        $defaultValue = $exampleValue = false;
116
                        break;
117 2
                    case $constraint instanceof Assert\IdenticalTo:
118
                        $defaultValue = $exampleValue = $constraint->value;
119
                        break;
120
                }
121 2
                $doc->setDefault($doc->getDefault() ?? $defaultValue);
122 2
                $doc->setExample($doc->getExample() ?? $exampleValue);
123
            }
124
        }
125
        // /!\ Payload doc will override values even if already defined
126 7
        $this->constraintPayloadDocHelper->appendPayloadDoc($doc, $constraint);
127 7
    }
128
129
    /**
130
     * @param TypeDoc $doc
131
     * @param Constraint $constraint
132
     */
133 7
    private function appendCollectionDoc(TypeDoc $doc, Constraint $constraint) : void
134
    {
135
        // If not a collection => give up
136 7
        if (!$doc instanceof CollectionDoc) {
137 7
            return;
138
        }
139
140 2
        if ($constraint instanceof Assert\Collection) {
141 2
            foreach ($constraint->fields as $fieldName => $constraintOrConstrainList) {
142 2
                if (is_array($constraintOrConstrainList)) {
143
                    $sibling = $this->transformList($constraintOrConstrainList);
144
                } else {
145 2
                    $sibling = $this->transform($constraintOrConstrainList);
146
                }
147 2
                $doc->addSibling(
148 2
                    $sibling->setName($fieldName)
149
                );
150
            }
151
152 2
            $doc->setAllowExtraSibling($constraint->allowExtraFields === true);
153 2
            $doc->setAllowMissingSibling($constraint->allowMissingFields === true);
154
        }
155 2
    }
156
157
    /**
158
     * @param TypeDoc $doc
159
     * @param Constraint $constraint
160
     */
161 7
    private function appendValidItemListDoc(TypeDoc $doc, Constraint $constraint) : void
162
    {
163 7
        if ($constraint instanceof Assert\Choice) {
164 2
            if ($constraint->callback && is_callable($constraint->callback)) {
165 1
                $choiceList = call_user_func($constraint->callback);
166
            } else {
167 1
                $choiceList = $constraint->choices ?? [];
168
            }
169 2
            foreach ($choiceList as $choice) {
170 2
                $this->addToAllowedValueListIfNotExist($doc, $choice);
171
            }
172 5
        } elseif ($constraint instanceof Assert\IsNull) {
173
            $this->addToAllowedValueListIfNotExist($doc, null);
174 5
        } elseif ($constraint instanceof Assert\IdenticalTo) {
175
            $this->addToAllowedValueListIfNotExist($doc, $constraint->value);
176 5
        } elseif ($constraint instanceof Assert\IsTrue) {
177
            $this->addToAllowedValueListIfNotExist($doc, true);
178
            $this->addToAllowedValueListIfNotExist($doc, 1);
179
            $this->addToAllowedValueListIfNotExist($doc, '1');
180 5
        } elseif ($constraint instanceof Assert\IsFalse) {
181
            $this->addToAllowedValueListIfNotExist($doc, false);
182
            $this->addToAllowedValueListIfNotExist($doc, 0);
183
            $this->addToAllowedValueListIfNotExist($doc, '0');
184 5
        } elseif ($constraint instanceof Assert\EqualTo) {
185
            $this->addToAllowedValueListIfNotExist($doc, $constraint->value);
186
        }
187 7
    }
188
189
    /**
190
     * @param ArrayDoc   $doc
191
     * @param Assert\All $constraint
192
     */
193 1
    private function appendAllConstraintToDoc(ArrayDoc $doc, Assert\All $constraint) : void
194
    {
195 1
        $itemDoc = $this->docTypeHelper->guess($constraint->constraints);
196 1
        foreach ($constraint->constraints as $subConstraint) {
197 1
            $this->appendToDoc($itemDoc, $subConstraint);
198
        }
199
200 1
        $doc->setItemValidation($itemDoc);
201 1
    }
202
203
    /**
204
     * @param       $object
205
     * @param array $classList
206
     *
207
     * @return bool
208
     */
209 7
    private function isInstanceOfOneClassIn($object, array $classList) : bool
210
    {
211 7
        $actualClassList = array_merge(
212 7
            [get_class($object)],
213 7
            class_implements($object),
214 7
            class_uses($object)
215
        );
216 7
        $parentClass = get_parent_class($object);
217 7
        while (false !== $parentClass) {
218 7
            $actualClassList[] = $parentClass;
219 7
            $parentClass = get_parent_class($parentClass);
220
        }
221
222 7
        return count(array_intersect($actualClassList, $classList)) > 0;
223
    }
224
225 2
    private function addToAllowedValueListIfNotExist(TypeDoc $doc, $value)
226
    {
227 2
        if (!in_array($value, $doc->getAllowedValueList(), true)) {
228 2
            $doc->addAllowedValue($value);
229
        }
230 2
    }
231
}
232