Passed
Branch master (3daac1)
by Vincent
07:53
created

ArrayElementBuilder::form()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Bdf\Form\Aggregate;
4
5
use Bdf\Form\Choice\ArrayChoice;
6
use Bdf\Form\Choice\ChoiceBuilderTrait;
7
use Bdf\Form\Choice\ChoiceInterface;
8
use Bdf\Form\Choice\LazzyChoice;
9
use Bdf\Form\ElementBuilderInterface;
10
use Bdf\Form\ElementInterface;
11
use Bdf\Form\Leaf\BooleanElement;
12
use Bdf\Form\Leaf\Date\DateTimeElement;
13
use Bdf\Form\Leaf\FloatElement;
14
use Bdf\Form\Leaf\IntegerElement;
15
use Bdf\Form\Leaf\StringElement;
16
use Bdf\Form\Phone\PhoneElement;
17
use Bdf\Form\Registry\Registry;
18
use Bdf\Form\Registry\RegistryInterface;
19
use Bdf\Form\Util\TransformerBuilderTrait;
20
use Bdf\Form\Util\ValidatorBuilderTrait;
21
use Symfony\Component\Validator\Constraint;
22
use Symfony\Component\Validator\Constraints\Choice as ChoiceConstraint;
23
use Symfony\Component\Validator\Constraints\Count;
24
use Symfony\Component\Validator\Constraints\NotBlank;
25
26
/**
27
 * Builder for the array element
28
 *
29
 * <code>
30
 * $builder->array('names')->string()
31
 *     ->length(['min' => 3]) // Add "length" constraint to inner string
32
 *     ->count(['min' => 1, 'max' => 6]) // Add count constraint
33
 *     ->satisfyArray(new MyArrayConstraint()) // Add a constraint for the array
34
 * ;
35
 * </code>
36
 *
37
 * @see ArrayElement
38
 * @see FormBuilderInterface::array()
39
 *
40
 * @implements ElementBuilderInterface<ArrayElement>
41
 */
42
class ArrayElementBuilder implements ElementBuilderInterface
43
{
44
    use ChoiceBuilderTrait {
45
        ChoiceBuilderTrait::choices as protected baseChoices;
46
    }
47
48
    use TransformerBuilderTrait {
49
        transformer as arrayTransformer;
50
    }
51
52
    use ValidatorBuilderTrait {
53
        ValidatorBuilderTrait::satisfy as arrayConstraint;
54
    }
55
56
    /**
57
     * @var RegistryInterface
58
     */
59
    private $registry;
60
61
    /**
62
     * @var ElementBuilderInterface|null
63
     */
64
    private $element;
65
66
    /**
67
     * @var mixed
68
     */
69
    private $value;
70
71
72
    /**
73
     * ArrayBuilder constructor.
74
     *
75
     * @param RegistryInterface|null $registry
76
     */
77 38
    public function __construct(RegistryInterface $registry = null)
78
    {
79 38
        $this->registry = $registry ?? new Registry();
80 38
    }
81
82
    /**
83
     * {@inheritdoc}
84
     *
85
     * Define a constraint on the inner element
86
     */
87 3
    public function satisfy($constraint, $options = null, bool $append = true)
88
    {
89 3
        $this->getElementBuilder()->satisfy($constraint, $options, $append);
90
91 3
        return $this;
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     *
97
     * Define a transformer on the inner element
98
     */
99 4
    public function transformer($transformer, bool $append = true)
100
    {
101 4
        $this->getElementBuilder()->transformer($transformer, $append);
102
103 4
        return $this;
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109 1
    public function value($value)
110
    {
111 1
        $this->value = $value;
112
113 1
        return $this;
114
    }
115
116
    /**
117
     * Define the inner element
118
     *
119
     * <code>
120
     * $builder->array('phones')->element(PhoneElement::class, function (PhoneElementBuilder $builder) {
121
     *     $builder->regionInput('../../address/country');
122
     * });
123
     * </code>
124
     *
125
     * @param class-string<ElementInterface> $element The element class name
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<ElementInterface> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<ElementInterface>.
Loading history...
126
     * @param callable|null $configurator Callback for configure the inner element builder. Takes as parameter the element builder
127
     *
128
     * @return $this
129
     */
130 37
    public function element(string $element, ?callable $configurator = null): ArrayElementBuilder
131
    {
132
        // @todo exception if already defined ?
133 37
        $this->element = $this->registry->elementBuilder($element);
134
135 37
        if ($configurator) {
136 9
            $configurator($this->element);
137
        }
138
139 37
        return $this;
140
    }
141
142
    /**
143
     * Get the inner element builder
144
     *
145
     * @return ElementBuilderInterface
146
     */
147 37
    public function getElementBuilder(): ElementBuilderInterface
148
    {
149 37
        if (!$this->element) {
150 25
            $this->element(StringElement::class);
151
        }
152
153 37
        return $this->element;
154
    }
155
156
    /**
157
     * Define as array of string
158
     *
159
     * <code>
160
     * $builder->array('names')->string(function (StringElementBuilder $builder) {
161
     *     $builder->length(['min' => 3, 'max' => 32])->regex('/[a-z -]+/i');
162
     * });
163
     * </code>
164
     *
165
     * @param callable|null $configurator Callback for configure the inner element builder
166
     *
167
     * @return $this
168
     */
169 1
    public function string(?callable $configurator = null): ArrayElementBuilder
170
    {
171 1
        return $this->element(StringElement::class, $configurator);
172
    }
173
174
    /**
175
     * Define as array of integer
176
     *
177
     * <code>
178
     * $builder->array('ids')->integer(function (IntegerElementBuilder $builder) {
179
     *     $builder->min(1)->max(9999);
180
     * });
181
     * </code>
182
     *
183
     * @param callable|null $configurator Callback for configure the inner element builder
184
     *
185
     * @return $this
186
     */
187 2
    public function integer(?callable $configurator = null): ArrayElementBuilder
188
    {
189 2
        return $this->element(IntegerElement::class, $configurator);
190
    }
191
192
    /**
193
     * Define as array of float
194
     *
195
     * <code>
196
     * $builder->array('prices')->float(function (FloatElementBuilder $builder) {
197
     *     $builder->min(0.01)->scale(2);
198
     * });
199
     * </code>
200
     *
201
     * @param callable|null $configurator Callback for configure the inner element builder
202
     *
203
     * @return $this
204
     */
205 2
    public function float(?callable $configurator = null): ArrayElementBuilder
206
    {
207 2
        return $this->element(FloatElement::class, $configurator);
208
    }
209
210
    /**
211
     * Define as array of boolean
212
     *
213
     * <code>
214
     * $builder->array('flags')->boolean();
215
     * </code>
216
     *
217
     * @param callable|null $configurator Callback for configure the inner element builder
218
     *
219
     * @return $this
220
     */
221 1
    public function boolean(?callable $configurator = null): ArrayElementBuilder
222
    {
223 1
        return $this->element(BooleanElement::class, $configurator);
224
    }
225
226
    /**
227
     * Define as array of date time
228
     *
229
     * <code>
230
     * $builder->array('dates')->dateTime(function (DateTimeElementBuilder $builder) {
231
     *     $builder->after(new DateTime());
232
     * });
233
     * </code>
234
     *
235
     * @param callable|null $configurator Callback for configure the inner element builder
236
     *
237
     * @return $this
238
     */
239 2
    public function dateTime(?callable $configurator = null): ArrayElementBuilder
240
    {
241 2
        return $this->element(DateTimeElement::class, $configurator);
242
    }
243
244
    /**
245
     * Define as array of phone number
246
     *
247
     * <code>
248
     * $builder->array('phones')->phone(function (PhoneElementBuilder $builder) {
249
     *     $builder->regionInput('../../address/country');
250
     * });
251
     * </code>
252
     *
253
     * @param callable|null $configurator Callback for configure the inner element builder
254
     *
255
     * @return $this
256
     */
257 1
    public function phone(?callable $configurator = null): ArrayElementBuilder
258
    {
259 1
        return $this->element(PhoneElement::class, $configurator);
260
    }
261
262
    /**
263
     * Define as array of embedded forms
264
     *
265
     * <code>
266
     * $builder->array('addresses')->form(function (FormBuilder $builder) {
267
     *     $builder->string('address');
268
     *     $builder->string('city');
269
     *     $builder->string('zipcode');
270
     *     $builder->string('country');
271
     * });
272
     * </code>
273
     *
274
     * @param callable|null $configurator Configure the embedded form
275
     *
276
     * @return $this
277
     */
278 1
    public function form(?callable $configurator = null): ArrayElementBuilder
279
    {
280 1
        return $this->element(Form::class, $configurator);
281
    }
282
283
    /**
284
     * Add a count constraint on the array
285
     *
286
     * Ex: `$builder->count(['min' => 3, 'max' => 5])`
287
     *
288
     * @param array $options Constraint options. Keys are "min", "max"
289
     *
290
     * @return $this
291
     *
292
     * @see Count For the list of options
293
     */
294 3
    public function count(array $options): ArrayElementBuilder
295
    {
296 3
        return $this->arrayConstraint(new Count($options));
297
    }
298
299
    /**
300
     * {@inheritdoc}
301
     *
302
     * @return $this
303
     */
304 6
    final public function required($options = null)
305
    {
306 6
        if (!$options instanceof Constraint) {
307 5
            if (is_string($options)) {
308 1
                $options = ['message' => $options];
309
            }
310
311 5
            $options = new NotBlank($options);
312
        }
313
314 6
        return $this->arrayConstraint($options);
315
    }
316
317
    /**
318
     * {@inheritdoc}
319
     */
320 3
    final public function choices($choices, $options = null): self
321
    {
322
        // @fixme c/c choice from trait
323 3
        if (!$choices instanceof ChoiceInterface) {
324 3
            $choices = is_array($choices) ? new ArrayChoice($choices) : new LazzyChoice($choices);
325
        }
326
327 3
        if (is_string($options)) {
328
            $options = ['message' => $options, 'multipleMessage' => $options];
329
        }
330
331 3
        $options['callback'] = [$choices, 'values'];
332 3
        $options['multiple'] = true;
333
334 3
        $this->choices = $choices;
335
336 3
        return $this->arrayConstraint(new ChoiceConstraint($options));
337
    }
338
339
    /**
340
     * {@inheritdoc}
341
     *
342
     * @return ArrayElement
343
     */
344 37
    public function buildElement(): ElementInterface
345
    {
346 37
        $element = new ArrayElement(
347 37
            $this->getElementBuilder()->buildElement(),
348 37
            $this->buildTransformer(),
349 37
            $this->buildValidator(),
350 37
            $this->getChoices()
351
        );
352
353 37
        if ($this->value) {
354 1
            $element->import($this->value);
355
        }
356
357 37
        return $element;
358
    }
359
360
    /**
361
     * Forward call to the inner element builder
362
     *
363
     * @param string $name
364
     * @param array $arguments
365
     *
366
     * @return $this
367
     */
368 1
    public function __call($name, $arguments)
369
    {
370 1
        $this->getElementBuilder()->$name(...$arguments);
371
372 1
        return $this;
373
    }
374
375
    /**
376
     * {@inheritdoc}
377
     */
378 37
    protected function registry(): RegistryInterface
379
    {
380 37
        return $this->registry;
381
    }
382
}
383