Slide 1

Slide 1 text

Bernhard Schussek · webmozart.io 2/122 http://slightlyviral.com/23-examples-of-why-women-live-longer-than-men/ Symfony2 Forms Symfony2 Forms Dos And Don'ts Dos And Don'ts Bernhard Schussek (@webmozart) Bernhard Schussek (@webmozart) Symfony Live Köln 2015 Symfony Live Köln 2015

Slide 2

Slide 2 text

Bernhard Schussek · webmozart.io 5/122 Bernhard Schussek Freelancer, Trainer and Coach Symfony Architecture Coding Practices webmozart.io

Slide 3

Slide 3 text

Bernhard Schussek · webmozart.io 6/122 @webmozart

Slide 4

Slide 4 text

Bernhard Schussek · webmozart.io 8/122 What's the hardest part of Symfony?

Slide 5

Slide 5 text

Bernhard Schussek · webmozart.io 9/122

Slide 6

Slide 6 text

Bernhard Schussek · webmozart.io 10/122

Slide 7

Slide 7 text

Bernhard Schussek · webmozart.io 11/122

Slide 8

Slide 8 text

Bernhard Schussek · webmozart.io 12/122

Slide 9

Slide 9 text

Bernhard Schussek · webmozart.io 13/122 Make simple cases simple, make complex cases possible ✓

Slide 10

Slide 10 text

Bernhard Schussek · webmozart.io 14/122 New in 2.7/2.8/3.0

Slide 11

Slide 11 text

Bernhard Schussek · webmozart.io 15/122 No More Type Names (>= 2.8)

Slide 12

Slide 12 text

Bernhard Schussek · webmozart.io 16/122 $form = $this->createForm(new PlaceOrderType()); $form = $this->createForm('app_place_order');

Slide 13

Slide 13 text

Bernhard Schussek · webmozart.io 17/122 $form = $this->createForm(PlaceOrderType::class);

Slide 14

Slide 14 text

Bernhard Schussek · webmozart.io 18/122 $builder ->add('firstName', 'text') ->add('lastName', 'text') ->add('address', 'app_address') ->add('shippingMethod', 'entity', [ // ... ]) ;

Slide 15

Slide 15 text

Bernhard Schussek · webmozart.io 19/122 $builder ->add('firstName', TextType::class) ->add('lastName', TextType::class) ->add('address', AddressType::class) ->add('shippingMethod', EntityType::class, [ // ... ]) ;

Slide 16

Slide 16 text

Bernhard Schussek · webmozart.io 20/122

Slide 17

Slide 17 text

Bernhard Schussek · webmozart.io 21/122

Slide 18

Slide 18 text

Bernhard Schussek · webmozart.io 22/122 class PlaceOrderType extends AbstractType { public function buildForm(FormBuilderInterface $buil { // ... } public function getName() { return 'app_place_order'; } }

Slide 19

Slide 19 text

Bernhard Schussek · webmozart.io 23/122 class PlaceOrderType extends AbstractType { public function buildForm(FormBuilderInterface $buil { // ... } }

Slide 20

Slide 20 text

Bernhard Schussek · webmozart.io 24/122 app.form.place_order: class: AppBundle\Form\PlaceOrderType tags: - { name: form.type, alias: app_place_order } app.form.submit_payment: class: AppBundle\Form\SubmitPaymentType tags: - { name: form.type, alias: app_submit_payment }

Slide 21

Slide 21 text

Bernhard Schussek · webmozart.io 25/122 app.form.process_order: class: AppBundle\Form\ProcessOrderType arguments: - @app.shipping_service tags: - { name: form.type, alias: app_process_order }

Slide 22

Slide 22 text

Bernhard Schussek · webmozart.io 26/122 app.form.process_order: class: AppBundle\Form\ProcessOrderType arguments: - @app.shipping_service tags: - { name: form.type }

Slide 23

Slide 23 text

Bernhard Schussek · webmozart.io 27/122 {% block app_place_order_widget %} {# ... #} {% endblock %} class PlaceOrderType extends AbstractType { // ... }

Slide 24

Slide 24 text

Bernhard Schussek · webmozart.io 28/122 {% block place_order_widget %} {# ... #} {% endblock %} class PlaceOrderType extends AbstractType { // ... }

Slide 25

Slide 25 text

Bernhard Schussek · webmozart.io 29/122 {% block app_place_order_widget %} {# ... #} {% endblock %} class PlaceOrderType extends AbstractType { // ... public function getBlockPrefix() { return 'app_place_order'; } }

Slide 26

Slide 26 text

Bernhard Schussek · webmozart.io 30/122 configureOptions() (>= 2.7)

Slide 27

Slide 27 text

Bernhard Schussek · webmozart.io 31/122 class PlaceOrderType extends AbstractType { public function setDefaultOptions( OptionsResolverInterface $resolver) { // ... } }

Slide 28

Slide 28 text

Bernhard Schussek · webmozart.io 32/122 class PlaceOrderType extends AbstractType { public function configureOptions( OptionsResolver $resolver) { // ... } }

Slide 29

Slide 29 text

Bernhard Schussek · webmozart.io 33/122 $resolver->setRequired('payment_method'); $resolver->setAllowedTypes('is_payable', 'bool'); $resolver->setAllowedValues( 'payment_method', PaymentMethod::all() ); // ...

Slide 30

Slide 30 text

Bernhard Schussek · webmozart.io 34/122 "prototype_data" option (>= 2.8)

Slide 31

Slide 31 text

Bernhard Schussek · webmozart.io 35/122 $builder->add('lineItems', CollectionType::class, [ 'type' => OrderLineType::class, 'prototype_data' => new OrderLine( 'New item', 120.00 ), ]);

Slide 32

Slide 32 text

Bernhard Schussek · webmozart.io 36/122 Return null from "query_builder" (>= 2.8)

Slide 33

Slide 33 text

Bernhard Schussek · webmozart.io 37/122 $builder->add('shippingMethod', EntityType::class, [ 'class' => ShippingMethod::class, 'query_builder' => function ($repo) use ($address) { if (!$address) { return null; } return $repo->queryByAddress($address); ), ]);

Slide 34

Slide 34 text

Bernhard Schussek · webmozart.io 38/122 Flipped "choices" option (>= 2.7)

Slide 35

Slide 35 text

Bernhard Schussek · webmozart.io 39/122 $builder->add('expressShipping', ChoiceType::class, [ 'choices' => [ '' => 'Decide later', 0 => 'No', 1 => 'Yes', ], ]);

Slide 36

Slide 36 text

Bernhard Schussek · webmozart.io 40/122 $builder->add('expressShipping', ChoiceType::class, [ 'choices' => [ 'Decide later' => null, 'No' => false, 'Yes' => true, ], ]);

Slide 37

Slide 37 text

Bernhard Schussek · webmozart.io 41/122 $builder->add('expressShipping', ChoiceType::class, [ 'choices' => [ 'Decide later' => Selection::get(LATER), 'No' => Selection::get(NO), 'Yes' => Selection::get(YES), ], ]);

Slide 38

Slide 38 text

Bernhard Schussek · webmozart.io 42/122 $builder->add('expressShipping', ChoiceType::class, [ 'choices' => [ 'Decide later' => Selection::get(LATER), 'No' => Selection::get(NO), 'Yes' => Selection::get(YES), ], 'choices_as_values' => true, ]); 2.8 only

Slide 39

Slide 39 text

Bernhard Schussek · webmozart.io 43/122 New "choice_label" option (>= 2.7)

Slide 40

Slide 40 text

Bernhard Schussek · webmozart.io 44/122 $builder->add('expressShipping', ChoiceType::class, [ // ... // $shipping->getTitle() 'choice_label' => 'title', ]); Decide later No Yes

Slide 41

Slide 41 text

Bernhard Schussek · webmozart.io 45/122 $builder->add('expressShipping', ChoiceType::class, [ // ... 'choice_label' => function (Selection $selection) { return humanize($selection->getType()); }, ]); Decide later No Yes

Slide 42

Slide 42 text

Bernhard Schussek · webmozart.io 46/122 New "choice_value" option (>= 2.7)

Slide 43

Slide 43 text

Bernhard Schussek · webmozart.io 47/122 $builder->add('expressShipping', ChoiceType::class, [ // ... ]); Decide later No Yes

Slide 44

Slide 44 text

Bernhard Schussek · webmozart.io 48/122 $builder->add('expressShipping', ChoiceType::class, [ // ... // $selection->getType() 'choice_value' => 'type' ]); Decide later No Yes

Slide 45

Slide 45 text

Bernhard Schussek · webmozart.io 49/122 $builder->add('expressShipping', ChoiceType::class, [ // ... 'choice_value' => function (Selection $selection) { return strtoupper($selection->getType()); }, ]); Decide later No Yes

Slide 46

Slide 46 text

Bernhard Schussek · webmozart.io 50/122 New "choice_attr" option (>= 2.7)

Slide 47

Slide 47 text

Bernhard Schussek · webmozart.io 51/122 $builder->add('expressShipping', ChoiceType::class, [ // ... 'choice_attr' => ['data-reference' => 1], ]); Decide later No Yes

Slide 48

Slide 48 text

Bernhard Schussek · webmozart.io 52/122 $builder->add('expressShipping', ChoiceType::class, [ // ... 'choice_attr' => function (Selection $selection) { return ['data-reference' => $selection->getRef() }, ]); Decide later No Yes

Slide 49

Slide 49 text

Bernhard Schussek · webmozart.io 53/122 New "group_by" option (>= 2.7)

Slide 50

Slide 50 text

Bernhard Schussek · webmozart.io 54/122 $builder->add('paymentMethod', ChoiceType::class, [ 'choices' => [ 'Credit Card' => [ 'VISA' => PaymentMethod::VISA, 'MasterCard' => PaymentMethod::MASTER_CARD, ], 'Bank Transfer' => [ // ... ], ], ]); ... ...

Slide 51

Slide 51 text

Bernhard Schussek · webmozart.io 55/122 $builder->add('paymentMethod', ChoiceType::class, [ // ... // $paymentMethod->getCategory() 'group_by' => 'category', ]); ... ...

Slide 52

Slide 52 text

Bernhard Schussek · webmozart.io 56/122 $builder->add('paymentMethod', ChoiceType::class, [ // ... // $paymentMethod->getCategory()->getName() 'group_by' => 'category.name', ]); ... ...

Slide 53

Slide 53 text

Bernhard Schussek · webmozart.io 57/122 $builder->add('paymentMethod', ChoiceType::class, [ // ... 'group_by' => function (PaymentMethod $method) { return sprintf( '%s (%s)', $method->getCategory()->getName(), $method->getCategory()->getFees() ); }, ]); ... ...

Slide 54

Slide 54 text

Bernhard Schussek · webmozart.io 58/122 Forms are not as hard as you think (even in 2.7)

Slide 55

Slide 55 text

Bernhard Schussek · webmozart.io 59/122 PlaceOrderType Controller FormFactory createForm() buildForm() FormBuilder Form

Slide 56

Slide 56 text

Bernhard Schussek · webmozart.io 60/122 PlaceOrderType FormFactory Controller createForm() buildForm() Form handleRequest() Order Validator createView() FormBuilder Form Order FormView update validate()

Slide 57

Slide 57 text

Bernhard Schussek · webmozart.io 61/122 PlaceOrderType FormFactory Controller createForm() buildForm() Form handleRequest() Order Validator createView() Twig FormBuilder Form Order FormView FormView update validate() render()

Slide 58

Slide 58 text

Bernhard Schussek · webmozart.io 62/122 PlaceOrderType FormFactory Controller createForm() buildForm() Form handleRequest() Order Validator createView() Twig FormBuilder Form Order FormView FormView update validate() render()

Slide 59

Slide 59 text

Bernhard Schussek · webmozart.io 63/122 PlaceOrderType FormFactory Controller createForm() buildForm() Form handleRequest() Order Validator createView() Twig FormBuilder Form Order FormView FormView update validate() render()

Slide 60

Slide 60 text

Bernhard Schussek · webmozart.io 64/122 PlaceOrderType FormFactory Controller createForm() buildForm() Form handleRequest() Order Validator createView() Twig FormBuilder Form Order FormView FormView update validate() render()

Slide 61

Slide 61 text

Bernhard Schussek · webmozart.io 65/122 PlaceOrderType FormFactory Controller createForm() buildForm() Form handleRequest() Order Validator createView() Twig FormBuilder Form Order FormView FormView update validate() render()

Slide 62

Slide 62 text

Bernhard Schussek · webmozart.io 66/122 PlaceOrderType FormFactory Controller createForm() buildForm() Form handleRequest() Order Validator createView() Twig FormBuilder Form Order FormView FormView update validate() render()

Slide 63

Slide 63 text

Bernhard Schussek · webmozart.io 67/122 My Best Practices

Slide 64

Slide 64 text

Bernhard Schussek · webmozart.io 68/122 PlaceOrderType FormFactory Controller createForm() buildForm() Form handleRequest() Order Validator createView() Twig FormBuilder Form Order FormView FormView update validate() render()

Slide 65

Slide 65 text

Bernhard Schussek · webmozart.io 69/122 Where to Create Forms? In the Controller Custom Form Type Custom Form Type

Slide 66

Slide 66 text

Bernhard Schussek · webmozart.io 70/122 How to Create Form Types? doctrine:generate:form by hand by hand

Slide 67

Slide 67 text

Bernhard Schussek · webmozart.io 71/122 ✓ Use PhpStorm

Slide 68

Slide 68 text

Bernhard Schussek · webmozart.io 72/122 Where to Submit Forms to? Same Action Different Action Same Action

Slide 69

Slide 69 text

Bernhard Schussek · webmozart.io 73/122 $form = $this->createForm(PaymentType::class, $order, [ 'action' => $this->generateUrl('payment_gw'), ]); ✓ Use "action" option in the controller

Slide 70

Slide 70 text

Bernhard Schussek · webmozart.io 74/122 $form = $this->createForm(PaymentType::class, $order, [ 'method' => 'PUT', ]); ✓ Use "method" option in the controller

Slide 71

Slide 71 text

Bernhard Schussek · webmozart.io 75/122 $defaultShipping = $this->getDefaultShipping($customer); $form = $this->createForm( new PlaceOrderType($defaultShipping), ); ✗ Don't Pass Dynamic Data to Constructor

Slide 72

Slide 72 text

Bernhard Schussek · webmozart.io 76/122 $defaultShipping = $this->getDefaultShipping($customer); $form = $this->createForm(PlaceOrderType::class, .., [ 'default_shipping' => $defaultShipping, ]); ✓ Use Custom Options

Slide 73

Slide 73 text

Bernhard Schussek · webmozart.io 77/122 parameters: app.us_shipping.enabled: true services: app.form.shipping: class: AppBundle\Form\ShippingType arguments: - %app.us_shipping.enabled% tags: - { type: form.type } ✓ Pass Global Settings To Constructor

Slide 74

Slide 74 text

Bernhard Schussek · webmozart.io 78/122 class ShippingType extends AbstractType { private $usShipping; public function __construct($usShipping) { $this->usShipping = (bool) $usShipping; } // ... } ✓ Pass Global Settings To Constructor

Slide 75

Slide 75 text

Bernhard Schussek · webmozart.io 79/122 PlaceOrderType FormFactory Controller createForm() buildForm() Form handleRequest() Order Validator createView() Twig FormBuilder Form Order FormView FormView update validate() render()

Slide 76

Slide 76 text

Bernhard Schussek · webmozart.io 80/122 {{ form(form) }} ✓ Use form(form) for Rapid Prototyping

Slide 77

Slide 77 text

Bernhard Schussek · webmozart.io 81/122 {{ form_row(form.firstName) }} {{ form_row(form.lastName) }} {{ form_row(form.address) }} {{ form_row(form.submit) }} ✓ Use form_row() to Control Field Order

Slide 78

Slide 78 text

Bernhard Schussek · webmozart.io 82/122 {{ form_row(form.address, { 'label': 'Your Address', 'attr': {'class': 'shippable-address'}, 'label_attr': {'data-id': 5} }) }} ✓ Set Labels/Attributes in the View

Slide 79

Slide 79 text

Bernhard Schussek · webmozart.io 83/122 ✓ Use the Form Debugger

Slide 80

Slide 80 text

Bernhard Schussek · webmozart.io 84/122
{{ form_errors(form.paymentMethod) }} Payment Method {{ form_widget(form.paymentMethod) }}
✓ Write Custom HTML By Hand

Slide 81

Slide 81 text

Bernhard Schussek · webmozart.io 85/122 {% form_theme form 'my-theme.html.twig' %} {{ form(form) }} ✗ (Mostly) Don't Use Themes

Slide 82

Slide 82 text

Bernhard Schussek · webmozart.io 86/122 PlaceOrderType FormFactory Controller createForm() buildForm() Form handleRequest() Order Validator createView() Twig FormBuilder Form Order FormView FormView update validate() render()

Slide 83

Slide 83 text

Bernhard Schussek · webmozart.io 87/122 /** * @Assert\NotNull * @Assert\GreaterThan(0) */ private $cost; ✗ Don't Add "Assert" Alias

Slide 84

Slide 84 text

Bernhard Schussek · webmozart.io 88/122 ✓ Use PhpStorm with the Annotation Plugin

Slide 85

Slide 85 text

Bernhard Schussek · webmozart.io 89/122 /** * @NotNull * @GreaterThan(0) */ private $cost; ✓ Auto-Import Annotations

Slide 86

Slide 86 text

Bernhard Schussek · webmozart.io 90/122 /** * @NotNull * @Expression("value < this.getThreshold()") */ private $memory; ✓ Use the @Expression Constraint

Slide 87

Slide 87 text

Bernhard Schussek · webmozart.io 91/122 /** * @Callback */ public function validateTotal( ExecutionContextInterface $context) { if ($this->total !== $this->getProductsTotal()) { $this->context->addViolation('Invalid.'); } } ✓ Use the @Callback Constraint

Slide 88

Slide 88 text

Bernhard Schussek · webmozart.io 92/122 $context->addViolation('product.total.invalid'); ✓ Use Message Keys

Slide 89

Slide 89 text

Bernhard Schussek · webmozart.io 93/122 $context->buildViolation('product.total.invalid') ->setParameter('{{ expected }}', $expected) ->addViolation(); ✓ Use Message Parameters

Slide 90

Slide 90 text

Bernhard Schussek · webmozart.io 94/122 /** * @NotNull * @GreaterThan(0, groups="Checkout") */ private $total; ✓ Use Validation Groups for Partial Validation

Slide 91

Slide 91 text

Bernhard Schussek · webmozart.io 95/122 public function configureOptions(OptionsResolver $resolv { $resolver->setDefaults([ 'validation_groups' => new GroupSequence([ 'Default', 'VatCheck' ]), ]); } ✓ Use Group Sequences for Sequential Validation

Slide 92

Slide 92 text

Bernhard Schussek · webmozart.io 96/122 class ProductName extends Regex { public function __construct($options = []) { parent::__construct(array_replace([ 'pattern' => '/^\w.*$/' ], $options); } public function validatedBy() { return RegexValidator::class; } } ✓ Create Domain-Specific Constraints

Slide 93

Slide 93 text

Bernhard Schussek · webmozart.io 97/122 class ProductNameValidator extends ConstraintValidator { public function validated($value, Constraint $constr { $this->context->getValidator() ->inContext($this->context) ->validate($value, new Regex(self::PATTERN)) ->validate($value, new NotEqualTo('keyword') ; } } ✓ Create Composite Constraints

Slide 94

Slide 94 text

Bernhard Schussek · webmozart.io 98/122 class ProductNameValidator extends ConstraintValidator { public function validated($value, Constraint $constr { if (null === $value) { return; } // ... } } ✓ Ignore Null Values

Slide 95

Slide 95 text

Bernhard Schussek · webmozart.io 99/122 PlaceOrderType FormFactory Controller createForm() buildForm() Form handleRequest() Order Validator createView() Twig FormBuilder Form Order FormView FormView update validate() render()

Slide 96

Slide 96 text

Bernhard Schussek · webmozart.io 100/122 Clark Kent ... Submit Name VAT $order->getCustomerName() $order->setCustomerName() Order +orderNumber +customerName +vat 1 2 3

Slide 97

Slide 97 text

Bernhard Schussek · webmozart.io 101/122 $builder->add('vat', TextType::class, [ 'property_path' => 'customer.vatId' ]); ✓ Decouple Forms from the Model Structure

Slide 98

Slide 98 text

Bernhard Schussek · webmozart.io 102/122 'customer.name' == $data->getCustomer()->setName() 'items[0].amount' == $data->getItems()[0]->setAmount() ✓ Learn Property Paths

Slide 99

Slide 99 text

Bernhard Schussek · webmozart.io 103/122 class Customer { public function __construct($name, Address $address) { // ... } } What About Non-Empty Constructors?

Slide 100

Slide 100 text

Bernhard Schussek · webmozart.io 104/122 public function configureOptions(OptionsResolver $resolv { $resolver->setDefaults([ 'empty_data' => function (FormInterface $form) { return new Customer( $form->get('name')->getData(), $form->get('address')->getData() ); }, ]); } ✓ Use "empty_data" for Non-Empty Constructors

Slide 101

Slide 101 text

Bernhard Schussek · webmozart.io 105/122 class Money { public function __construct($amount) { $this->amount = $amount; } public function getAmount() { return $this->amount; } } What About Value Objects?

Slide 102

Slide 102 text

Bernhard Schussek · webmozart.io 106/122 Model Format View Format DateTime string DateTimeType

Slide 103

Slide 103 text

Bernhard Schussek · webmozart.io 107/122 Model Format View Format Money string MyMoneyType

Slide 104

Slide 104 text

Bernhard Schussek · webmozart.io 108/122 class MyMoneyType extends AbstractType implements DataTransformerInterface { public function buildForm( FormBuilderInterface $builder, array $options) { $builder->addModelTransformer($this); } public function getParent() { return MoneyType::class; } // ... } ✓ Use Data Transformers (1/3)

Slide 105

Slide 105 text

Bernhard Schussek · webmozart.io 109/122 class MyMoneyType extends AbstractType implements DataTransformerInterface { // ... public function transform($money) { if (null === $money) { return null; } return $money->getAmount(); } // ... } ✓ Use Data Transformers (2/3)

Slide 106

Slide 106 text

Bernhard Schussek · webmozart.io 110/122 class MyMoneyType extends AbstractType implements DataTransformerInterface { // ... public function reverseTransform($amount) { if (null === $amount) { return null; } return new Money($amount); } } ✓ Use Data Transformers (3/3)

Slide 107

Slide 107 text

Bernhard Schussek · webmozart.io 111/122 class Customer { public function relocate(Address $address) { // ... } } What About Non-Standard Methods?

Slide 108

Slide 108 text

Bernhard Schussek · webmozart.io 112/122 Clark Kent ... Submit Name VAT $order->getCustomerName() $order->setCustomerName() Order +orderNumber +customerName +vat 1 2 3

Slide 109

Slide 109 text

Bernhard Schussek · webmozart.io 113/122 class CustomerType extends AbstractType implements DataMapperInterface { public function buildForm( FormBuilderInterface $builder, array $options) { $builder->setDataMapper($this); } // ... } ✓ Use Data Mappers (1/3)

Slide 110

Slide 110 text

Bernhard Schussek · webmozart.io 114/122 class CustomerType extends AbstractType implements DataMapperInterface { // ... public function mapDataToForms($customer, $forms) { $forms = iterator_to_array($forms); $forms['address']->setData($customer->getAddress // ... } // ... } ✓ Use Data Mappers (2/3)

Slide 111

Slide 111 text

Bernhard Schussek · webmozart.io 115/122 class CustomerType extends AbstractType implements DataMapperInterface { // ... public function mapFormsToData($forms, &$customer) { $forms = iterator_to_array($forms); $customer->relocate($forms['address']->getData() // ... } // ... } ✓ Use Data Mappers (3/3)

Slide 112

Slide 112 text

Bernhard Schussek · webmozart.io 116/122 class Order { public function setCustomerNumber(int $orderNumber) { // ... } } What About Strict Models?

Slide 113

Slide 113 text

Bernhard Schussek · webmozart.io 117/122 public function configureOptions(OptionsResolver $resolv { $resolver->setDefaults([ 'data_class' => PlaceOrder::class, ]); } class PlaceOrder { public $orderNumber; public $customerNumber; // ... } ✓ Map to DTOs

Slide 114

Slide 114 text

Bernhard Schussek · webmozart.io 118/122 class PlaceOrder { /** @OrderNumber */ public $orderNumber; } /** @Entity */ class Order { /** @OrderNumber */ private $orderNumber; } ✓ Use Domain-Specific Constraints

Slide 115

Slide 115 text

Bernhard Schussek · webmozart.io 119/122 public function configureOptions(OptionsResolver $resolv { $resolver->setDefaults([ 'data_class' => Order::class, ]); } /** * @Entity */ class Order { ... } ✓ Map to Doctrine Entities in RAD Applications

Slide 116

Slide 116 text

Bernhard Schussek · webmozart.io 120/122 PlaceOrderType FormFactory Controller createForm() buildForm() Form handleRequest() Order Validator createView() Twig FormBuilder Form Order FormView FormView update validate() render()

Slide 117

Slide 117 text

Bernhard Schussek · webmozart.io 121/122 $builder ->add('country', 'entity', [ ... ]) ->add('province', 'entity', [ ... ]) ; What About Field Dependencies?

Slide 118

Slide 118 text

Bernhard Schussek · webmozart.io 122/122 $builder->get('country')->addEventListener( FormEvents::POST_SET_DATA, $addProvinceField ); $builder->get('country')->addEventListener( FormEvents::POST_SUBMIT, $addProvinceField ); ✓ Use POST_* Hooks On Fields (1/2)

Slide 119

Slide 119 text

Bernhard Schussek · webmozart.io 123/122 $addProvinceField = function (FormEvent $event) { $form = $event->getForm()->getParent(); $country = $event->getData(); $form->add('province', EntityType::class, [ 'class' => Province::class, 'query_builder' => function ($repo) use ($countr // query provinces by $country }, ]); }; ✓ Use POST_* Hooks On Fields (2/2)

Slide 120

Slide 120 text

Bernhard Schussek · webmozart.io 124/122 Forms can be simple if done right!

Slide 121

Slide 121 text

Bernhard Schussek · webmozart.io 125/122 Complex things are complex...

Slide 122

Slide 122 text

Bernhard Schussek · webmozart.io 126/122 Questions? Questions? joind.in/talk/4beec joind.in/talk/4beec Bernhard Schussek Bernhard Schussek @webmozart @webmozart webmozart.io