symfony form basics - oromeetup #3 cherkassy
TRANSCRIPT
Presentation title here
Symfony Form
“Make simple cases simple, make complex cases possible”
- Bernhard Schussek
presented by Andreii Yatsenko
Presentation title here
About me
● Я Андрюха● PHP Developer at Oro Inc.● Live in Kyiv● Studied at GeekHub ~3 years ago● 6 years with PHP
Presentation title here
Agenda
● Form Flow● Form EventListeners● Form Extensions● Data Transformers● Form Tests
● Bad and Good Practices● Almost W/O code samples
Presentation title here
Form flow
● create form● set data to form● handle request● Profit! do something with updated data
○ E.g. persist data to DB
Presentation title here
How to create form?At first ● you need to create formFactory● or if you are using Symfony just get it from
ServiceContainer
$formFactory = $container->get(“form.factory”);
Presentation title here
How to create form?
● 1) using FormBuilder in Controller :($formFactory->createFormBuilder()
->add(‘firstName”)->add(“lastName”)->add(“email”);
Presentation title here
How to create form?
● 2) using FormType
Presentation title here
How to create form?
● If you in Controller$this->createForm($formType)// or$this->createFormBuilder()
->add(“name”, “text”)->getForm();
Presentation title here
How to set form data?
Presentation title here
Always set form data at form creation!
$data = new User();$formFactory
->createForm($formType, $data, $options);// or$formFactory->createFormBuilder($data);
Presentation title here
Try to not setData after form creation
Because● All form events will be triggered twice :(
$form = $formFactory->createForm($formType);
$form->setData($data);
Presentation title here
Always use objects instead of arrays!
Presentation title here
If you don’t have object - you just not created it yet!
● Create DTO
Presentation title here
Why objects, I like kittens arrays?
Presentation title here
Because of ValidationValidation
Presentation title here
How to Validate Form
● Do not validate form!● Validate data
○ yaml, xml, annotation drivers for constrants
● But what if my form doesn’t submit the data? ○ like “Apply terms checkbox”
● Make field mapped = false and validate form
Presentation title here
$formFactory->createForm($formType, $data, $options);
Form options (configuration)
Presentation title here
Form options (configuration)
● Form options is a form configuration. And it’s a data too, data that needed for building and submitting the form, but it’s not the Form Data (Usually don’t need to be updated from user input)
Presentation title here
Form options (configuration)
● Buy the way Form Data is an option too. You can just set $options[‘data’], and form factory internally use it too. It’s have higher priority than $data (second param) in
$formFactory->createForm($type, $data, $options)
Presentation title here
Next
Some form internals
Presentation title here
How form saves your memory
In Symfony2 all form fields are forms too, and can be reused.
E.x. ● TextFormType● CategoryFormType● ProductFormType● etc.
Presentation title here
How form saves your memory
Form composed from ● Form (form builder template etc.) and● FormConfig (options with data)
● Form and FormConfig are different objects.
Presentation title here
How form saves your memory
When you reuse Form, e.x. if you create form with 20 text fields, only ONE TextForm will be created.
Presentation title here
How form saves your memory
● But for different fields you have different data, and usually different options, they are so called FormConfig and will be created for each fields, so in our example you will have 20 FormConfig objects
Presentation title here
How to create form, that depends on data?● Use dependency injections:inject data to FormType container
E.x. new FormType($someData);
Presentation title here
• How to create form, that depends on data?● Use dependency injections:inject data to FormType container
E.x. new FormType($someData);
Presentation title here
Never inject dinamic objects to FormType!
Presentation title here
Never inject dinamic objects to FormType!
Because you will change FormType, not the FormConfig, so all forms on page will be configured the same.
Presentation title here
The right way :)
● Inject to container static objects E.x. Doctine, not concrete Entities
Presentation title here
If I don’t have static object? :(
● Use custom form options
$formFactory->createFrom($type, $data, $options);
At first you need to configure them in FormType::configureOptions()
Presentation title here
But wait, Where to place dinamic business logic, if I can’t place it in FormType?
:(
Presentation title here
But wait, Where to place dinamic business logic, if I can’t place it in FormType?
In EventListeners! :)
Presentation title here
Form Events
● PRE_SET_DATA● POST_SET_DATA
● PRE_SUBMIT● SUBMIT● POST_SUBMIT
Presentation title here
How to subscribe to form events?
1) Create:● EventListener - any callable● EventSubscriber - separate class
implementedSymfony\Component\EventDispatcher\EventSubscriberInterface
2) Add them to FormBuilder
$builder->addEventListener(“PRE_SUBMIT”, $listener);
$builder->addEventSubscriber($subscriber)
Presentation title here
You can even change the form in EventListeners● add or remove form fields
http://symfony.com/doc/current/components/form/form_events.html
Presentation title here
You can even change the form in EventListeners● but in Symfony you can’t change or
replace form field
In OroPlatform we have FormUtils that solve this problemhttps://github.com/orocrm/platform/blob/936c1bfcff34c2381a31fa40726d518d3f6ab0b8/src/Oro/Bundle/FormBundle/Utils/FormUtils.php
Presentation title here
But you can’t register event listener like in symfony or doctrine EventDispatcher, from another bundle
Presentation title here
How to solve this?
Presentation title here
How to solve this?
FormTypeExtensions
Presentation title here
FormTypeExtensions● Like form types but have getExtendedType()
method● extends Symfony\Component\Form\AbstractTypeExtension
● Registered in DI with tag form.type_extension
Presentation title here
How form works with data internally?
Presentation title here
How form works with data internally?
E.x. DateTimeFormType● You set to form timestamp
● User will see human formatted string
Presentation title here
How form works with data internally?
E.x. DateTimeFormType● You set to form timestamp● In EventListeners you work with DateTime ● User will see human formatted string
Presentation title here
How form works with data internally?
E.x.● Controller - timestamp● Form - DateTime ● User - formatted string
Presentation title here
How form works with data internally?
E.x.● Model Data - timestamp● Normalized Data - DateTime ● View Data - formatted string
Presentation title here
Data Transformers● Model Transformer● View Transformer
implement Symfony\Component\Form\DataTransformerInterface
2 methods:● transform($data)● reverseTransform($data)
Presentation title here
Data Transformers
Register at $builder
$builder->get('date') ->addModelTransformer($transformer);
Presentation title here
Data Transformers
● Should only transform data from one format to another, but never change the data.
● If you want to change data use EventListeners
Presentation title here
Debugging Forms
Presentation title here
How to test forms
Unit Test$builder = $this->createMock(“FormBuilder”);$builder->expects($this->at(1))->method(“add”)->withArguments(“username”, “text”)//...
Presentation title here
How to test forms
Unit Test$builder = $this->createMock(“FormBuilder”);$builder->expects($this->at(1))->method(“add”)->withArguments(“username”, “text”)//...
Presentation title here
How to test forms
● Write Integrational tests \Symfony\Component\Form\Test\FormIntegrationTestCaseE.x.https://github.com/laboro/b2b/blob/9d18a036099357b04d41116ca2dfcaa57a59163c/src/OroB2B/Bundle/AccountBundle/Tests/Unit/Form/Extension/CategoryFormExtensionTest.php#L34-L34
Presentation title here
Presentation title here
Agenda
● Form Flow● Form EventListeners● Form Extensions● Data Transformers● Form Tests
Presentation title here
?