Passed
Push — master ( 254c29...f2eb24 )
by Yo
03:16
created

ConstraintToParamsDocTransformer::appendToDoc()   C

Complexity

Conditions 13
Paths 9

Size

Total Lines 53
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 35
CRAP Score 13

Importance

Changes 0
Metric Value
cc 13
eloc 40
nc 9
nop 2
dl 0
loc 53
ccs 35
cts 35
cp 1
crap 13
rs 6.6166
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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