Completed
Push — master ( b0c988...0a4514 )
by Vincent
11:48
created

RootForm   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 257
Duplicated Lines 0 %

Test Coverage

Coverage 86.67%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 32
eloc 56
c 1
b 0
f 0
dl 0
loc 257
ccs 65
cts 75
cp 0.8667
rs 9.84

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A submit() 0 6 1
A patch() 0 6 1
A import() 0 5 1
A value() 0 3 1
A root() 0 3 1
A offsetSet() 0 3 1
A offsetGet() 0 3 1
A submitButton() 0 3 1
A httpValue() 0 15 3
A valid() 0 3 1
A getValidator() 0 7 2
A constraintGroups() 0 7 3
A getIterator() 0 3 1
A container() 0 3 1
A offsetExists() 0 3 1
A setContainer() 0 3 1
A view() 0 12 2
A submitToButtons() 0 7 4
A getPropertyAccessor() 0 7 2
A error() 0 3 1
A offsetUnset() 0 3 1
1
<?php
2
3
namespace Bdf\Form\Aggregate;
4
5
use BadMethodCallException;
6
use Bdf\Form\Button\ButtonInterface;
7
use Bdf\Form\Child\ChildInterface;
8
use Bdf\Form\Child\Http\HttpFieldPath;
9
use Bdf\Form\ElementInterface;
10
use Bdf\Form\Error\FormError;
11
use Bdf\Form\RootElementInterface;
12
use Bdf\Form\View\ElementViewInterface;
13
use Symfony\Component\PropertyAccess\PropertyAccess;
14
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
15
use Symfony\Component\Validator\Constraint;
16
use Symfony\Component\Validator\Validator\ValidatorInterface;
17
use Symfony\Component\Validator\ValidatorBuilder;
18
19
/**
20
 * Adapt a form element as root element
21
 * The root form handle constraint group, validator and property accessor instances, and submit button
22
 *
23
 * The root form should be used instead of the form element for `submit()`
24
 *
25
 * <code>
26
 * $form = new MyForm();
27
 *
28
 * $root = $form->root();
29
 * if (!$root->submit($request->post())->valid()) {
30
 *     throw new MyError();
31
 * }
32
 *
33
 * $entity = $root->value();
34
 *
35
 * switch ($btn = $root->submitButton() ? $btn->name() : null) {
36
 *     case 'save':
37
 *         return $this->save($entity);
38
 *
39
 *     case 'delete':
40
 *         return $this->delete($entity);
41
 *
42
 *     default:
43
 *         throw new InvalidAction();
44
 * }
45
 * </code>
46
 *
47
 * @todo delegation trait
48
 */
49
final class RootForm implements RootElementInterface, ChildAggregateInterface
50
{
51
    /**
52
     * @var Form
53
     */
54
    private $form;
55
56
    /**
57
     * @var ButtonInterface[]
58
     */
59
    private $buttons;
60
61
    /**
62
     * @var ButtonInterface|null
63
     */
64
    private $submitButton;
65
66
    /**
67
     * @var PropertyAccessorInterface|null
68
     */
69
    private $propertyAccessor;
70
71
    /**
72
     * @var ValidatorInterface|null
73
     */
74
    private $validator;
75
76
77
    /**
78
     * RootForm constructor.
79
     *
80
     * @param Form $form
81
     * @param ButtonInterface[] $buttons
82
     * @param PropertyAccessorInterface|null $propertyAccessor
83
     * @param ValidatorInterface|null $validator
84
     */
85 141
    public function __construct(Form $form, array $buttons = [], ?PropertyAccessorInterface $propertyAccessor = null, ?ValidatorInterface $validator = null)
86
    {
87 141
        $this->form = $form;
88 141
        $this->buttons = $buttons;
89 141
        $this->propertyAccessor = $propertyAccessor;
90 141
        $this->validator = $validator;
91 141
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96 26
    public function submit($data): ElementInterface
97
    {
98 26
        $this->submitToButtons($data);
99 26
        $this->form->submit($data);
100
101 26
        return $this;
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107 3
    public function patch($data): ElementInterface
108
    {
109 3
        $this->submitToButtons($data);
110 3
        $this->form->patch($data);
111
112 3
        return $this;
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118 2
    public function import($entity): ElementInterface
119
    {
120 2
        $this->form->import($entity);
121
122 2
        return $this;
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128 1
    public function value()
129
    {
130 1
        return $this->form->value();
131
    }
132
133
    /**
134
     * {@inheritdoc}
135
     */
136 2
    public function httpValue()
137
    {
138 2
        $httpValue = $this->form->httpValue();
139
140 2
        if (empty($this->buttons)) {
141 1
            return $httpValue;
142
        }
143
144 1
        $httpValue = (array) $httpValue;
145
146 1
        foreach ($this->buttons as $btn) {
147 1
            $httpValue += $btn->toHttp();
148
        }
149
150 1
        return $httpValue;
151
    }
152
153
    /**
154
     * {@inheritdoc}
155
     */
156 1
    public function valid(): bool
157
    {
158 1
        return $this->form->valid();
159
    }
160
161
    /**
162
     * {@inheritdoc}
163
     */
164 1
    public function error(?HttpFieldPath $field = null): FormError
165
    {
166 1
        return $this->form->error($field);
167
    }
168
169
    /**
170
     * {@inheritdoc}
171
     */
172 1
    public function container(): ?ChildInterface
173
    {
174 1
        return null; // root cannot have a container
175
    }
176
177
    /**
178
     * {@inheritdoc}
179
     */
180
    public function setContainer(ChildInterface $container): ElementInterface
181
    {
182
        throw new BadMethodCallException('Cannot wrap a root element into a container');
183
    }
184
185
    /**
186
     * {@inheritdoc}
187
     */
188 1
    public function root(): RootElementInterface
189
    {
190 1
        return $this;
191
    }
192
193
    /**
194
     * {@inheritdoc}
195
     */
196 5
    public function view(?HttpFieldPath $field = null): ElementViewInterface
197
    {
198 5
        $buttons = [];
199
200 5
        foreach ($this->buttons as $button) {
201 2
            $buttons[$button->name()] = $button->view($field);
202
        }
203
204 5
        $view = $this->form->view($field);
205 5
        $view->setButtons($buttons);
206
207 5
        return $view;
208
    }
209
210
    /**
211
     * {@inheritdoc}
212
     */
213 4
    public function submitButton(): ?ButtonInterface
214
    {
215 4
        return $this->submitButton;
216
    }
217
218
    /**
219
     * {@inheritdoc}
220
     */
221 64
    public function getValidator(): ValidatorInterface
222
    {
223 64
        if ($this->validator === null) {
224 63
            $this->validator = (new ValidatorBuilder())->getValidator();
225
        }
226
227 64
        return $this->validator;
228
    }
229
230
    /**
231
     * {@inheritdoc}
232
     */
233 75
    public function getPropertyAccessor(): PropertyAccessorInterface
234
    {
235 75
        if ($this->propertyAccessor === null) {
236 74
            $this->propertyAccessor = PropertyAccess::createPropertyAccessor();
237
        }
238
239 75
        return $this->propertyAccessor;
240
    }
241
242
    /**
243
     * {@inheritdoc}
244
     */
245 62
    public function constraintGroups(): array
246
    {
247 62
        if (!$button = $this->submitButton) {
248 61
            return [Constraint::DEFAULT_GROUP];
249
        }
250
251 2
        return $button->constraintGroups() ?: [Constraint::DEFAULT_GROUP];
252
    }
253
254
    /**
255
     * {@inheritdoc}
256
     */
257 6
    public function offsetGet($offset): ChildInterface
258
    {
259 6
        return $this->form[$offset];
260
    }
261
262
    /**
263
     * {@inheritdoc}
264
     */
265 4
    public function offsetExists($offset): bool
266
    {
267 4
        return isset($this->form[$offset]);
268
    }
269
270
    /**
271
     * {@inheritdoc}
272
     */
273
    public function offsetSet($offset, $value)
274
    {
275
        $this->form[$offset] = $value;
276
    }
277
278
    /**
279
     * {@inheritdoc}
280
     */
281
    public function offsetUnset($offset)
282
    {
283
        unset($this->form[$offset]);
284
    }
285
286
    /**
287
     * {@inheritdoc}
288
     */
289
    public function getIterator()
290
    {
291
        return $this->form->getIterator();
292
    }
293
294
    /**
295
     * Submit HTTP fields to buttons
296
     *
297
     * @param mixed $data The HTTP value
298
     */
299 27
    private function submitToButtons($data): void
300
    {
301 27
        $this->submitButton = null;
302
303 27
        foreach ($this->buttons as $button) {
304 4
            if ($button->submit($data) && $this->submitButton === null) {
305 4
                $this->submitButton = $button;
306
            }
307
        }
308 27
    }
309
}
310