Today we are going to tackle a problem that I’ve recently faced with Symfony form types.
Scenario: I need a choice type of a certain entity (Foo
) with values that follows a certain logic
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<?php class FooChoiceType extends AbstractType { private $fooAdapter; public function __construct(FooAdapter $fooAdapter) { $this->fooAdapter = $fooAdapter; } function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'required' => true, 'multiple' => false, 'choices' => $this->fooAdapter->getFoos(), ]); } public function getParent() { return ChoiceType::class; } } |
I’ve built up a FooChoiceType
because I don’t want to inject fooAdapter
in each form where I’m going to use FooChoiceType
.
All worked like a charm but after a while I noticed that getFoos
should accept a parameter (array) in order to filter Foo
objects by, for instance, an attribute (for this example, type
). Let’s say I need FooChoices
of type x
and y
in a FormType and FooChoices
of type z
in another FormType: it is pretty easy to understand that types should be passed as an option to FooChoices
from FormType
that use it.
Question: How can I pass to getFoos
an option directly into configureOptions
?
Answer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?php class FooChoiceType extends AbstractType { private $fooAdapter; public function __construct(FooAdapter $fooAdapter) { $this->fooAdapter = $fooAdapter; } public function configureOptions(OptionsResolver $resolver) { $types = function (Options $options, $choices) { return $choices ? $choices : $this->getChoices($options['types']); }; $resolver->setDefaults([ 'required' => true, 'multiple' => false, 'types' => [], ]); $resolver->setNormalizer('choices', $types); } private function getChoices(array $types) { return $this->fooAdapter->getFoos($types); } public function getParent() { return ChoiceType::class; } } |
How does this work?
By creating a closure
1 2 3 |
$types = function (Options $options, $choices) { return $choices ? $choices : $this->getChoices($options['types']); }; |
we are delaying choices
evaluation in order to populate it only when types
is available; if no types
is passed from FormType
(s) we have a fallback ([]
) value.
That’s it; super easy!
$choices = []; in line 29 is not used?
Yes, it’s a typo: I’m gonna fix it.
Thank you for spotting this.