Passed
Push — release/1.0.0 ( cf7b4c...70aa03 )
by Yo
01:28
created

ConstraintToParamsDocTransformer   A

Complexity

Total Complexity 37

Size/Duplication

Total Lines 208
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 99
dl 0
loc 208
ccs 101
cts 101
cp 1
rs 9.44
c 0
b 0
f 0
wmc 37

9 Methods

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