Child   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 244
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 22
eloc 63
c 1
b 0
f 0
dl 0
loc 244
ccs 71
cts 71
cp 1
rs 10

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 2
A view() 0 3 1
A import() 0 17 2
A __clone() 0 3 1
A httpFields() 0 3 1
A parent() 0 3 1
A element() 0 3 1
A patch() 0 8 3
A error() 0 3 1
A name() 0 3 1
A fill() 0 17 2
A dependencies() 0 3 1
A extractValue() 0 10 2
A submit() 0 5 1
A setParent() 0 11 2
1
<?php
2
3
namespace Bdf\Form\Child;
4
5
use Bdf\Form\Aggregate\ChildAggregateInterface;
6
use Bdf\Form\Child\Http\ArrayOffsetHttpFields;
7
use Bdf\Form\Child\Http\HttpFieldPath;
8
use Bdf\Form\Child\Http\HttpFieldsInterface;
9
use Bdf\Form\ElementInterface;
10
use Bdf\Form\Error\FormError;
11
use Bdf\Form\Filter\FilterInterface;
12
use Bdf\Form\PropertyAccess\ExtractorInterface;
13
use Bdf\Form\PropertyAccess\HydratorInterface;
14
use Bdf\Form\Transformer\NullTransformer;
15
use Bdf\Form\Transformer\TransformerInterface;
16
use Bdf\Form\Util\HttpValue;
17
use Bdf\Form\View\ElementViewInterface;
18
use WeakReference;
19
20
/**
21
 * Child which extract HTTP field value from a simple array access
22
 */
23
final class Child implements ChildInterface
24
{
25
    /**
26
     * @var ElementInterface
27
     */
28
    private $element;
29
30
    /**
31
     * @var WeakReference<ChildAggregateInterface>
32
     */
33
    private $parent;
34
35
    /**
36
     * @var string
37
     */
38
    private $name;
39
40
    /**
41
     * @var HttpFieldsInterface
42
     */
43
    private $fields;
44
45
    /**
46
     * @var mixed
47
     */
48
    private $defaultValue;
49
50
    /**
51
     * @var FilterInterface[]
52
     */
53
    private $filters;
54
55
    /**
56
     * @var HydratorInterface|null
57
     */
58
    private $hydrator;
59
60
    /**
61
     * @var ExtractorInterface|null
62
     */
63
    private $extractor;
64
65
    /**
66
     * @var string[]
67
     */
68
    private $dependencies;
69
70
    /**
71
     * @var TransformerInterface
72
     */
73
    private $transformer;
74
75
76
    /**
77
     * ArrayOffsetChild constructor.
78
     *
79
     * @param string $name
80
     * @param ElementInterface $element
81
     * @param HttpFieldsInterface|null $fields
82
     * @param FilterInterface[] $filters
83
     * @param mixed $defaultValue
84
     * @param HydratorInterface|null $hydrator
85
     * @param ExtractorInterface|null $extractor
86
     * @param string[] $dependencies
87
     */
88 335
    public function __construct(string $name, ElementInterface $element, ?HttpFieldsInterface $fields = null, array $filters = [], $defaultValue = null, ?HydratorInterface $hydrator = null, ?ExtractorInterface $extractor = null, array $dependencies = [], ?TransformerInterface $transformer = null)
89
    {
90 335
        $this->name = $name;
91 335
        $this->element = $element->setContainer($this);
92 335
        $this->fields = $fields ?: new ArrayOffsetHttpFields($name);
93 335
        $this->defaultValue = $defaultValue;
94 335
        $this->filters = $filters;
95 335
        $this->hydrator = $hydrator;
96 335
        $this->extractor = $extractor;
97 335
        $this->dependencies = $dependencies;
98 335
        $this->transformer = $transformer ?? NullTransformer::instance();
99 335
    }
100
101
    /**
102
     * {@inheritdoc}
103
     */
104 206
    public function element(): ElementInterface
105
    {
106 206
        return $this->element;
107
    }
108
109
    /**
110
     * {@inheritdoc}
111
     *
112
     * @psalm-suppress NullableReturnStatement
113
     * @psalm-suppress InvalidNullableReturnType
114
     */
115 94
    public function parent(): ChildAggregateInterface
116
    {
117 94
        return $this->parent->get();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->parent->get() could return the type null which is incompatible with the type-hinted return Bdf\Form\Aggregate\ChildAggregateInterface. Consider adding an additional type-check to rule them out.
Loading history...
118
    }
119
120
    /**
121
     * {@inheritdoc}
122
     */
123 286
    public function setParent(ChildAggregateInterface $parent): ChildInterface
124
    {
125 286
        if ($this->parent === null) {
126 286
            $this->parent = WeakReference::create($parent);
127 286
            return $this;
128
        }
129
130 28
        $child = clone $this;
131 28
        $child->parent = WeakReference::create($parent);
132
133 28
        return $child;
134
    }
135
136
    /**
137
     * {@inheritdoc}
138
     */
139 228
    public function name(): string
140
    {
141 228
        return $this->name;
142
    }
143
144
    /**
145
     * {@inheritdoc}
146
     */
147 157
    public function dependencies(): array
148
    {
149 157
        return $this->dependencies;
150
    }
151
152
    /**
153
     * {@inheritdoc}
154
     */
155 40
    public function import($entity): void
156
    {
157 40
        if (!$this->extractor) {
158 2
            return;
159
        }
160
161
        /** @psalm-suppress PossiblyNullReference */
162 39
        $propertyAccessor = $this->parent->get()->root()->getPropertyAccessor();
163
164 39
        $this->extractor->setPropertyAccessor($propertyAccessor);
165 39
        $this->extractor->setFormElement($this);
166
167 39
        $value = $this->extractor->extract($entity);
168 39
        $value = $this->transformer->transformToHttp($value, $this->element);
169 39
        $this->extractor->setFormElement(null);
170
171 39
        $this->element->import($value);
172 39
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177 55
    public function fill(&$entity): void
178
    {
179 55
        if (!$this->hydrator) {
180 2
            return;
181
        }
182
183
        /** @psalm-suppress PossiblyNullReference */
184 53
        $propertyAccessor = $this->parent->get()->root()->getPropertyAccessor();
185
186 53
        $this->hydrator->setPropertyAccessor($propertyAccessor);
187 53
        $this->hydrator->setFormElement($this);
188
189 53
        $value = $this->element->value();
190 53
        $value = $this->transformer->transformFromHttp($value, $this->element);
191
192 53
        $this->hydrator->hydrate($entity, $value);
193 53
        $this->hydrator->setFormElement(null);
194 53
    }
195
196
    /**
197
     * {@inheritdoc}
198
     */
199 131
    public function submit($data): bool
200
    {
201 131
        $value = $this->extractValue($data);
202
203 131
        return $this->element->submit($value)->valid();
204
    }
205
206
    /**
207
     * {@inheritdoc}
208
     */
209 12
    public function patch($data): bool
210
    {
211 12
        $value = $data !== null && $this->fields->contains($data)
212 9
            ? $this->extractValue($data)
213 12
            : null
214
        ;
215
216 12
        return $this->element->patch($value)->valid();
217
    }
218
219
    /**
220
     * {@inheritdoc}
221
     */
222 9
    public function httpFields(): array
223
    {
224 9
        return $this->fields->format($this->element->httpValue());
225
    }
226
227
    /**
228
     * {@inheritdoc}
229
     */
230 45
    public function error(?HttpFieldPath $field = null): FormError
231
    {
232 45
        return $this->element->error($this->fields->get($field));
233
    }
234
235
    /**
236
     * {@inheritdoc}
237
     */
238 14
    public function view(?HttpFieldPath $field = null): ElementViewInterface
239
    {
240 14
        return $this->element->view($this->fields->get($field));
241
    }
242
243
    /**
244
     * {@inheritdoc}
245
     */
246 28
    public function __clone()
247
    {
248 28
        $this->element = $this->element->setContainer($this);
249 28
    }
250
251
    /**
252
     * Extract HTTP value and apply filters
253
     *
254
     * @param mixed $httpValue
255
     * @return mixed The filtered value
256
     */
257 139
    private function extractValue($httpValue)
258
    {
259 139
        $value = $this->fields->extract($httpValue);
260 139
        $default = $this->defaultValue;
261
262 139
        foreach ($this->filters as $filter) {
263 90
            $value = $filter->filter($value, $this, $default);
264
        }
265
266 139
        return HttpValue::orDefault($value, $default);
267
    }
268
}
269