Skip to content

Commit 12c9507

Browse files
committed
add a pizza size dependant field
pizza size is only required when the meal is dinner and the food pizza. this introduces a 2nd level of dependant fields. Meal -> Food -> Pizza size. Here, we need to use `FormFactoryInterface::createNamedBuilder()` to get access to `addEventListener()`, as that method is not available on `FormInterface`.
1 parent 8c5518c commit 12c9507

File tree

6 files changed

+156
-12
lines changed

6 files changed

+156
-12
lines changed

ux.symfony.com/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"symfony/asset-mapper": "6.3.x-dev",
2121
"symfony/console": "6.3.*",
2222
"symfony/dotenv": "6.3.*",
23+
"symfony/expression-language": "6.3.*",
2324
"symfony/flex": "^2",
2425
"symfony/form": "6.3.*",
2526
"symfony/framework-bundle": "6.3.x-dev",

ux.symfony.com/composer.lock

Lines changed: 65 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ux.symfony.com/src/Enum/PizzaSize.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace App\Enum;
4+
5+
enum PizzaSize: int
6+
{
7+
case Small = 12;
8+
case Medium = 14;
9+
case Large = 16;
10+
11+
public function getReadable(): string
12+
{
13+
return match ($this) {
14+
self::Small => '12 inch',
15+
self::Medium => '14 inch',
16+
self::Large => '16 inch',
17+
};
18+
}
19+
}

ux.symfony.com/src/Form/MealPlannerForm.php

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,30 @@
44

55
use App\Enum\Food;
66
use App\Enum\Meal;
7+
use App\Enum\PizzaSize;
78
use App\Model\MealPlan;
89
use Symfony\Component\Form\AbstractType;
910
use Symfony\Component\Form\Extension\Core\Type\EnumType;
1011
use Symfony\Component\Form\FormBuilderInterface;
1112
use Symfony\Component\Form\FormEvent;
1213
use Symfony\Component\Form\FormEvents;
14+
use Symfony\Component\Form\FormFactoryInterface;
1315
use Symfony\Component\Form\FormInterface;
1416
use Symfony\Component\OptionsResolver\OptionsResolver;
1517

1618
class MealPlannerForm extends AbstractType
1719
{
20+
private FormFactoryInterface $factory;
21+
1822
/**
1923
* @var array<string, mixed>
2024
*/
2125
private $dependencies = [];
2226

2327
public function buildForm(FormBuilderInterface $builder, array $options)
2428
{
29+
$this->factory = $builder->getFormFactory();
30+
2531
$builder->add('meal', EnumType::class, [
2632
'class' => Meal::class,
2733
'choice_label' => fn (Meal $meal): string => $meal->getReadable(),
@@ -47,6 +53,7 @@ public function onPreSetData(FormEvent $event): void
4753
$data = $event->getData();
4854

4955
$this->addFoodField($event->getForm(), $data?->getMeal());
56+
$this->addPizzaSizeField($event->getForm(), $data?->getPizzaSize());
5057
}
5158

5259
public function storeDependencies(FormEvent $event): void
@@ -62,16 +69,47 @@ public function onPostSubmitMeal(FormEvent $event): void
6269
);
6370
}
6471

72+
public function onPostSubmitFood(FormEvent $event): void
73+
{
74+
$this->addPizzaSizeField(
75+
$event->getForm()->getParent(),
76+
$this->dependencies['mainFood'],
77+
);
78+
79+
$this->dependencies = [];
80+
}
81+
6582
public function addFoodField(FormInterface $form, ?Meal $meal): void
6683
{
67-
$form->add('mainFood', EnumType::class, [
68-
'class' => Food::class,
69-
'placeholder' => null === $meal ? 'Select a meal first' : sprintf('What\'s for %s?', $meal->getReadable()),
70-
'choices' => $meal?->getFoodChoices(),
71-
'choice_label' => fn (Food $food): string => $food->getReadable(),
72-
'disabled' => null === $meal,
73-
// silence real-time "invalid" message when switching "meals"
74-
'invalid_message' => false,
84+
$mainFood = $this->factory
85+
->createNamedBuilder('mainFood', EnumType::class, $meal, [
86+
'class' => Food::class,
87+
'placeholder' => null === $meal ? 'Select a meal first' : sprintf('What\'s for %s?', $meal->getReadable()),
88+
'choices' => $meal?->getFoodChoices(),
89+
'choice_label' => fn (Food $food): string => $food->getReadable(),
90+
'disabled' => null === $meal,
91+
// silence real-time "invalid" message when switching "meals"
92+
'invalid_message' => false,
93+
'autocomplete' => true,
94+
'auto_initialize' => false,
95+
])
96+
->addEventListener(FormEvents::POST_SUBMIT, [$this, 'storeDependencies'])
97+
->addEventListener(FormEvents::POST_SUBMIT, [$this, 'onPostSubmitFood']);
98+
99+
$form->add($mainFood->getForm());
100+
}
101+
102+
public function addPizzaSizeField(FormInterface $form, ?Food $food): void
103+
{
104+
if (Food::Pizza !== $food) {
105+
return;
106+
}
107+
108+
$form->add('pizzaSize', EnumType::class, [
109+
'class' => PizzaSize::class,
110+
'placeholder' => 'What size pizza?',
111+
'choice_label' => fn (PizzaSize $pizzaSize): string => $pizzaSize->getReadable(),
112+
'required' => true,
75113
'autocomplete' => true,
76114
]);
77115
}

ux.symfony.com/src/Model/MealPlan.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,25 @@
44

55
use App\Enum\Food;
66
use App\Enum\Meal;
7-
use Symfony\Component\Validator\Constraints\NotBlank;
7+
use App\Enum\PizzaSize;
8+
use Symfony\Component\Validator\Constraints as Assert;
89

910
class MealPlan
1011
{
11-
#[NotBlank]
12+
#[Assert\NotNull]
1213
private ?Meal $meal = null;
1314

14-
#[NotBlank]
15+
#[Assert\NotNull]
1516
private ?Food $mainFood = null;
1617

18+
#[Assert\When(
19+
expression: 'this.getMainFood() === enum("App\\\Enum\\\Food::Pizza")',
20+
constraints: [
21+
new Assert\NotNull(message: 'Please select a Pizza Size.'),
22+
],
23+
)]
24+
private ?PizzaSize $pizzaSize = null;
25+
1726
public function getMeal(): ?Meal
1827
{
1928
return $this->meal;
@@ -33,4 +42,14 @@ public function setMainFood(?Food $mainFood): void
3342
{
3443
$this->mainFood = $mainFood;
3544
}
45+
46+
public function getPizzaSize(): ?PizzaSize
47+
{
48+
return $this->pizzaSize;
49+
}
50+
51+
public function setPizzaSize(?PizzaSize $pizzaSize): void
52+
{
53+
$this->pizzaSize = $pizzaSize;
54+
}
3655
}

ux.symfony.com/templates/components/MealPlanner.html.twig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,8 @@
55
{{ form_start(form) }}
66
{{ form_row(form.meal) }}
77
{{ form_row(form.mainFood) }}
8+
{% if form.pizzaSize is defined %}
9+
{{ form_row(form.pizzaSize) }}
10+
{% endif %}
811
{{ form_end(form) }}
912
</div>

0 commit comments

Comments
 (0)