Registry::elementBuilder()   A
last analyzed

Complexity

Conditions 6
Paths 12

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 13
c 1
b 0
f 0
dl 0
loc 25
ccs 13
cts 13
cp 1
rs 9.2222
cc 6
nc 12
nop 1
crap 6
1
<?php
2
3
namespace Bdf\Form\Registry;
4
5
use Bdf\Form\Aggregate\ArrayChildBuilder;
6
use Bdf\Form\Aggregate\ArrayElement;
7
use Bdf\Form\Aggregate\ArrayElementBuilder;
8
use Bdf\Form\Aggregate\Form;
9
use Bdf\Form\Aggregate\FormBuilder;
10
use Bdf\Form\Button\ButtonBuilderInterface;
11
use Bdf\Form\Button\SubmitButtonBuilder;
12
use Bdf\Form\Child\ChildBuilder;
13
use Bdf\Form\Child\ChildBuilderInterface;
14
use Bdf\Form\Constraint\Closure;
15
use Bdf\Form\Csrf\CsrfElement;
16
use Bdf\Form\Csrf\CsrfElementBuilder;
17
use Bdf\Form\Custom\CustomForm;
18
use Bdf\Form\Custom\CustomFormBuilder;
19
use Bdf\Form\ElementBuilderInterface;
20
use Bdf\Form\Filter\ClosureFilter;
21
use Bdf\Form\Filter\FilterInterface;
22
use Bdf\Form\Leaf\AnyElement;
23
use Bdf\Form\Leaf\AnyElementBuilder;
24
use Bdf\Form\Leaf\BooleanElement;
25
use Bdf\Form\Leaf\BooleanElementBuilder;
26
use Bdf\Form\Leaf\Date\DateTimeChildBuilder;
27
use Bdf\Form\Leaf\Date\DateTimeElement;
28
use Bdf\Form\Leaf\Date\DateTimeElementBuilder;
29
use Bdf\Form\Leaf\FloatElement;
30
use Bdf\Form\Leaf\FloatElementBuilder;
31
use Bdf\Form\Leaf\Helper\EmailElement;
32
use Bdf\Form\Leaf\Helper\EmailElementBuilder;
33
use Bdf\Form\Leaf\Helper\UrlElement;
34
use Bdf\Form\Leaf\Helper\UrlElementBuilder;
35
use Bdf\Form\Leaf\IntegerElement;
36
use Bdf\Form\Leaf\IntegerElementBuilder;
37
use Bdf\Form\Leaf\StringElement;
38
use Bdf\Form\Leaf\StringElementBuilder;
39
use Bdf\Form\Phone\PhoneChildBuilder;
40
use Bdf\Form\Phone\PhoneElement;
41
use Bdf\Form\Phone\PhoneElementBuilder;
42
use Bdf\Form\Transformer\ClosureTransformer;
43
use Bdf\Form\Transformer\DataTransformerAdapter;
44
use Bdf\Form\Transformer\TransformerInterface;
45
use InvalidArgumentException;
46
use LogicException;
47
use Symfony\Component\Form\DataTransformerInterface;
48
use Symfony\Component\Validator\Constraint;
49
50
/**
51
 * Base registry interface
52
 */
53
class Registry implements RegistryInterface
54
{
55
    /**
56
     * @var class-string<ElementBuilderInterface>[]|callable[]
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<ElementBuil...Interface>[]|callable[] at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<ElementBuilderInterface>[]|callable[].
Loading history...
57
     */
58
    private $elementBuilderFactories = [
59
        StringElement::class => StringElementBuilder::class,
60
        IntegerElement::class => IntegerElementBuilder::class,
61
        FloatElement::class => FloatElementBuilder::class,
62
        BooleanElement::class => BooleanElementBuilder::class,
63
        AnyElement::class => AnyElementBuilder::class,
64
65
        EmailElement::class => EmailElementBuilder::class,
66
        UrlElement::class => UrlElementBuilder::class,
67
68
        CsrfElement::class => CsrfElementBuilder::class,
69
        PhoneElement::class => PhoneElementBuilder::class,
70
71
        DateTimeElement::class => DateTimeElementBuilder::class,
72
73
        ArrayElement::class => ArrayElementBuilder::class,
74
        Form::class => FormBuilder::class,
75
    ];
76
77
    /**
78
     * @var class-string<ChildBuilderInterface>[]|callable[]
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<ChildBuilderInterface>[]|callable[] at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<ChildBuilderInterface>[]|callable[].
Loading history...
79
     */
80
    private $childBuilderFactories = [
81
        DateTimeElement::class => DateTimeChildBuilder::class,
82
        PhoneElement::class => PhoneChildBuilder::class,
83
        ArrayElement::class => ArrayChildBuilder::class,
84
    ];
85
86
    /**
87
     * Registry constructor.
88
     */
89 398
    public function __construct()
90
    {
91
        $this->register(CustomForm::class, function (RegistryInterface $registry, string $formClass) {
92
            /** @psalm-suppress ArgumentTypeCoercion */
93 5
            return new CustomFormBuilder($formClass, $this->elementBuilder(Form::class));
94 398
        });
95 398
    }
96
97
    /**
98
     * {@inheritdoc}
99
     */
100 6
    public function filter($filter): FilterInterface
101
    {
102 6
        if ($filter instanceof FilterInterface) {
103 1
            return $filter;
104
        }
105
106 5
        if (is_callable($filter)) {
107 3
            return new ClosureFilter($filter);
108
        }
109
110
        // @todo container ?
111
        /** @var class-string<FilterInterface> $filter */
112 2
        return new $filter();
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118 203
    public function constraint($constraint): Constraint
119
    {
120 203
        if ($constraint instanceof Constraint) {
121 182
            return $constraint;
122
        }
123
124 24
        if (is_callable($constraint)) {
125 19
            return new Closure(['callback' => $constraint]);
126
        }
127
128 5
        if (is_array($constraint)) {
129 4
            $options = $constraint[1];
130 4
            $constraint = $constraint[0];
131
132 4
            if (is_string($options)) {
133 1
                $options = ['message' => $options];
134
            }
135
136
            /** @var class-string<Constraint> $constraint */
137 4
            return new $constraint($options);
138
        }
139
140
        /** @var class-string<Constraint> $constraint */
141 1
        return new $constraint();
142
    }
143
144
    /**
145
     * {@inheritdoc}
146
     */
147 54
    public function transformer($transformer): TransformerInterface
148
    {
149 54
        if ($transformer instanceof TransformerInterface) {
150 13
            return $transformer;
151
        }
152
153 41
        if ($transformer instanceof DataTransformerInterface) {
154 1
            return new DataTransformerAdapter($transformer);
155
        }
156
157 40
        if (is_callable($transformer)) {
158 39
            return new ClosureTransformer($transformer);
159
        }
160
161 1
        throw new LogicException('Invalid view transformer given for input '.var_export($transformer, true));
162
    }
163
164
    /**
165
     * {@inheritdoc}
166
     */
167 120
    public function childBuilder(string $element, string $name): ChildBuilderInterface
168
    {
169 120
        $elementBuilder = $this->elementBuilder($element);
170
171 120
        $builderFactory = $this->childBuilderFactories[$element] ?? ChildBuilder::class;
172
173 120
        if (is_string($builderFactory)) {
174
            /** @var class-string<ChildBuilderInterface> $builderFactory */
175 119
            return new $builderFactory($name, $elementBuilder, $this);
176
        }
177
178 1
        return $builderFactory($name, $elementBuilder, $this);
179
    }
180
181
    /**
182
     * {@inheritdoc}
183
     *
184
     * @psalm-param class-string<E> $element
185
     * @psalm-template E as \Bdf\Form\ElementInterface
186
     * @psalm-return ElementBuilderInterface<E>
187
     */
188 158
    public function elementBuilder(string $element): ElementBuilderInterface
189
    {
190 158
        $builderFactory = null;
191
192 158
        if (isset($this->elementBuilderFactories[$element])) {
193 156
            $builderFactory = $this->elementBuilderFactories[$element];
194
        } else {
195 7
            foreach ($this->elementBuilderFactories as $builderElement => $factory) {
196 7
                if (is_subclass_of($element, $builderElement, true)) {
197 6
                    $builderFactory = $factory;
198 6
                    break;
199
                }
200
            }
201
        }
202
203 158
        if (!$builderFactory) {
204 1
            throw new InvalidArgumentException('The element '.$element.' is not registered');
205
        }
206
207 157
        if (is_string($builderFactory)) {
208
            /** @var class-string<ElementBuilderInterface<E>> $builderFactory */
209 156
            return new $builderFactory($this, $element);
210
        }
211
212 6
        return ($builderFactory)($this, $element);
213
    }
214
215
    /**
216
     * {@inheritdoc}
217
     */
218 4
    public function buttonBuilder(string $name): ButtonBuilderInterface
219
    {
220 4
        return new SubmitButtonBuilder($name);
221
    }
222
223
    /**
224
     * Register a new element builder
225
     *
226
     * <code>
227
     * // Register MyCustomBuilder as builder for MyCustomElement
228
     * $registry->register(MyCustomElement::class, MyCustomBuilder::class);
229
     *
230
     * // Register a factory builder. The factory takes as parameters the registry, and the element class name
231
     * $registry->register(MyCustomElement::class, function (Registry $registry, string $element) {
232
     *     return new MyCustomBuilder($registry);
233
     * });
234
     *
235
     * // Register with a custom child builder
236
     * $registry->register(MyCustomElement::class, MyCustomBuilder::class, function (string $name, ElementBuilderInterface $builder, Registry $registry) {
237
     *     return new MyCustomChildBuilder($registry, new ChildBuilder($name, $builder, $registry));
238
     * });
239
     * </code>
240
     *
241
     * @param string $elementType The element class name
242
     * @param class-string<ElementBuilderInterface>|callable $builderFactory The builder factory, or builder class name
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<ElementBuilderInterface>|callable at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<ElementBuilderInterface>|callable.
Loading history...
243
     * @param class-string<ChildBuilderInterface>|callable|null $childBuilderFactory The builder factory for child, or builder class name. If null, use default child builder
244
     *
245
     * @see Registry::elementBuilder()
246
     */
247 398
    public function register(string $elementType, $builderFactory, $childBuilderFactory = null): void
248
    {
249 398
        $this->elementBuilderFactories[$elementType] = $builderFactory;
250
251 398
        if ($childBuilderFactory !== null) {
252 1
            $this->childBuilderFactories[$elementType] = $childBuilderFactory;
253
        }
254 398
    }
255
}
256