Everything you always wanted 
to know about forms* 
*but were afraid to ask 
Andrea Giuliano 
@bit_shark 
SYMFONYDAY 2013
Tree 
Abstract Data Structure
Tree: Abstract data Type 
Andrea Giuliano @bit_shark 
… 
collection of nodes each of which has an associated 
value and a ...
Symfony Forms are trees 
Andrea Giuliano @bit_shark 
Form 
Form Form Form 
Form
Example: a meeting form 
Andrea Giuliano @bit_shark
Let’s create it with Symfony 
namespace MyAppMyBundleForm;! 
! 
use SymfonyComponentFormAbstractType;! 
use SymfonyCompone...
Symfony’s point of view 
Andrea Giuliano @bit_shark 
Meeting 
form
Symfony’s point of view 
Andrea Giuliano @bit_shark 
Meeting 
form 
$builder->add('name', 'string') 
Name 
string
Andrea Giuliano @bit_shark 
$builder->add('when', 'date') 
Meeting 
form 
Name 
string 
Date 
date 
Month 
choice 
Day 
ch...
Andrea Giuliano @bit_shark 
Meeting 
form 
Name 
string 
Date 
date 
Month 
choice 
Day 
choice 
Year 
choice 
Symfony’s p...
Data format
Data format 
class Form implements IteratorAggregate, FormInterface! 
{! 
[...]! 
! 
/**! 
* The form data in model format...
Data format 
Model 
Data 
Andrea Giuliano @bit_shark 
Norm 
Data 
How the information 
View 
Data 
is represented in the a...
Data format 
Model 
Data 
Andrea Giuliano @bit_shark 
Norm 
Data 
View 
Data 
How the information 
is represented in the v...
Data format 
Model 
Data 
Andrea Giuliano @bit_shark 
Norm 
Data 
View 
Data 
???
Model Data and View Data 
$builder->add('when', 'date') 
Andrea Giuliano @bit_shark 
widget View Data 
choice array 
singl...
Model Data and View Data 
Andrea Giuliano @bit_shark 
widget View Data 
choice array 
single_text string 
input Model Data...
Normalized Data 
Which format would you like to play with 
Andrea Giuliano @bit_shark 
in your application logic? 
$form->...
Data Transformers
Data transformers 
class BooleanToStringTransformer implements DataTransformerInterface! 
{! 
private $trueValue;! 
! 
pub...
Data transformers 
Model 
Data 
Andrea Giuliano @bit_shark 
Norm 
Data 
View 
Data 
transform() transform() 
Model Transfo...
Data transformers 
class MeetingType extends AbstractType! 
{! 
public function buildForm(FormBuilderInterface $builder, a...
Data transformers 
class MeetingType extends AbstractType! 
{! 
public function buildForm(FormBuilderInterface $builder, a...
Andrea Giuliano @bit_shark
Data transformers 
<service id="dnsee.type.my_text" ! 
class="DnseeMyBundleFormTypeMyTextType">! 
<argument type="service"...
Data transformers 
class MyTextType extends AbstractType! 
{! 
private $myManager;! 
Andrea Giuliano @bit_shark 
Use it by...
Data transformers 
class MeetingType extends AbstractType! 
{! 
public function buildForm(FormBuilderInterface $builder, a...
Andrea Giuliano @bit_shark 
Remember 
Data Transformers transforms 
data representation 
Don’t use them to change 
data in...
Events
Andrea Giuliano @bit_shark 
To change data information use 
form events 
Events
Events 
class FacebookSubscriber implements EventSubscriberInterface! 
{! 
public static function getSubscribedEvents()! 
...
Events 
class RegistrationType extends AbstractType! 
{! 
public function buildForm(FormBuilderInterface $builder, array $...
Events 
class RegistrationForm extends AbstractType! 
{! 
public function buildForm(FormBuilderInterface $builder, array $...
Events 
$form->setData($symfonyDay); 
PRE_SET_DATA 
Model Norm View 
Andrea Giuliano @bit_shark 
meeting [Form] 
POST_SET_...
Events 
POST_BIND 
$form->handleRequest($request); 
Model Norm View 
Andrea Giuliano @bit_shark 
meeting [Form] 
child 
PR...
Test
namespace AcmeTestBundleTestsFormType;! 
! 
use DnseeEventBundleFormTypeEventType;! 
use DnseeEventBundleModelEventObject;...
namespace AcmeTestBundleTestsFormType;! 
! 
use DnseeEventBundleFormTypeEventType;! 
use DnseeEventBundleModelEvent;! 
use...
Test 
namespace AcmeTestBundleTestsFormType;! 
! 
use DnseeEventBundleFormTypeEventType;! 
use DnseeEventBundleModelEventO...
Test 
namespace AcmeTestBundleTestsFormType;! 
! 
use AcmeTestBundleFormTypeTestedType;! 
use AcmeTestBundleModelTestObjec...
Test 
namespace AcmeTestBundleTestsFormType;! 
! 
use DnseeEventBundleFormTypeEventType;! 
use DnseeEventBundleModelEventO...
Test 
namespace AcmeTestBundleTestsFormType;! 
! 
use DnseeEventBundleFormTypeEventType;! 
use DnseeEventBundleModelEventO...
?
http://joind.in/9784 
Andrea Giuliano 
@bit_shark
References 
https://speakerdeck.com/webmozart/symfony2-form-tricks 
http://www.flickr.com/photos/yahya/132963781/ 
http://...
Upcoming SlideShare
Loading in …5
×

Everything you always wanted to know about forms* *but were afraid to ask

6,112 views
5,923 views

Published on

La componente dei Form di Symfony2 rende possibile la costruzione di diverse tipologie di form in modo del tutto semplice. La sua architettura flessibile e altamente scalabile permette di poter gestire strutture adatte ad ogni tipo di esigenza. Tuttavia, conoscere come utilizzare appieno tutta la sua potenza non è banale. In questo talk verrà trattato in profondità la componente Form di Symfony2, mostrando i suoi meccanismi di base e come utilizzarli per estenderli ed introdurre la propria logica di business, così da costruire form cuciti a misura delle tue necessità.

Published in: Technology

Everything you always wanted to know about forms* *but were afraid to ask

  1. 1. Everything you always wanted to know about forms* *but were afraid to ask Andrea Giuliano @bit_shark SYMFONYDAY 2013
  2. 2. Tree Abstract Data Structure
  3. 3. Tree: Abstract data Type Andrea Giuliano @bit_shark … collection of nodes each of which has an associated value and a list of children connected to their parents by means of an edge
  4. 4. Symfony Forms are trees Andrea Giuliano @bit_shark Form Form Form Form Form
  5. 5. Example: a meeting form Andrea Giuliano @bit_shark
  6. 6. Let’s create it with Symfony namespace MyAppMyBundleForm;! ! use SymfonyComponentFormAbstractType;! use SymfonyComponentFormFormBuilderInterface;! use SymfonyComponentOptionsResolverOptionsResolverInterface;! ! class MeetingType extends AbstractType! {! public function buildForm(FormBuilderInterface $builder, array $option)! {! $builder->add('name', 'string');! $builder->add('when', 'date');! $builder->add('featured', 'checkbox');! }! ! public function getName()! {! return 'meeting';! }! } Andrea Giuliano @bit_shark
  7. 7. Symfony’s point of view Andrea Giuliano @bit_shark Meeting form
  8. 8. Symfony’s point of view Andrea Giuliano @bit_shark Meeting form $builder->add('name', 'string') Name string
  9. 9. Andrea Giuliano @bit_shark $builder->add('when', 'date') Meeting form Name string Date date Month choice Day choice Year choice Symfony’s point of view
  10. 10. Andrea Giuliano @bit_shark Meeting form Name string Date date Month choice Day choice Year choice Symfony’s point of view $builder->add('featured', 'checkbox') Featured checkbox
  11. 11. Data format
  12. 12. Data format class Form implements IteratorAggregate, FormInterface! {! [...]! ! /**! * The form data in model format! */! private $modelData;! ! /**! * The form data in normalized format! */! private $normData;! ! /**! * The form data in view format! */! private $viewData;! } Andrea Giuliano @bit_shark
  13. 13. Data format Model Data Andrea Giuliano @bit_shark Norm Data How the information View Data is represented in the application model
  14. 14. Data format Model Data Andrea Giuliano @bit_shark Norm Data View Data How the information is represented in the view domain
  15. 15. Data format Model Data Andrea Giuliano @bit_shark Norm Data View Data ???
  16. 16. Model Data and View Data $builder->add('when', 'date') Andrea Giuliano @bit_shark widget View Data choice array single_text string input Model Data string string datetime DateTime array array timestamp integer Meeting form Date date
  17. 17. Model Data and View Data Andrea Giuliano @bit_shark widget View Data choice array single_text string input Model Data string string datetime DateTime array array timestamp integer Meeting form Date date What $form->getData() will return?
  18. 18. Normalized Data Which format would you like to play with Andrea Giuliano @bit_shark in your application logic? $form->getNormData()
  19. 19. Data Transformers
  20. 20. Data transformers class BooleanToStringTransformer implements DataTransformerInterface! {! private $trueValue;! ! public function __construct($trueValue)! {! $this->trueValue = $trueValue;! }! ! public function transform($value)! {! if (null === $value) {! return null;! }! ! if (!is_bool($value)) {! throw new TransformationFailedException('Expected a Boolean.');! }! ! return $value ? $this->trueValue : null;! }! ! public function reverseTransform($value)! {! if (null === $value) {! return false;! }! ! if (!is_string($value)) {! throw new TransformationFailedException('Expected a string.');! }! ! return true;! }! } Andrea Giuliano @bit_shark
  21. 21. Data transformers Model Data Andrea Giuliano @bit_shark Norm Data View Data transform() transform() Model Transformer View Transformer reverseTransform() reverseTransform()
  22. 22. Data transformers class MeetingType extends AbstractType! {! public function buildForm(FormBuilderInterface $builder, array $option)! {! $transformer = new MyDataTransformer();! ! $builder->add('name', 'string');! $builder->add('when', 'date')->addModelTransformer($transformer);! $builder->add('featured', 'checkbox');! }! ! public function getName()! {! return 'meeting';! }! }! Add a ModelTransformer or a ViewTransformer Andrea Giuliano @bit_shark
  23. 23. Data transformers class MeetingType extends AbstractType! {! public function buildForm(FormBuilderInterface $builder, array $option)! {! $transformer = new MyDataTransformer(/*AnAwesomeDependence*/);! ! $builder->add('name', 'string');! $builder->add('when', 'date')->addModelTransformer($transformer);! $builder->add('featured', 'checkbox');! }! ! public function getName()! {! return 'meeting';! }! }! Andrea Giuliano @bit_shark in case of dependencies?
  24. 24. Andrea Giuliano @bit_shark
  25. 25. Data transformers <service id="dnsee.type.my_text" ! class="DnseeMyBundleFormTypeMyTextType">! <argument type="service" id="dnsee.my_awesome_manager"/>! <tag name="form.type" alias="my_text" />! Andrea Giuliano @bit_shark Use it by creating your own type </service>
  26. 26. Data transformers class MyTextType extends AbstractType! {! private $myManager;! Andrea Giuliano @bit_shark Use it by creating your own type ! public function __construct(MyManager $manager)! {! $this->myManager = $manager;! }! ! public function buildForm(FormBuilderInterface $builder, array $options)! {! $transformer = new MyTransformer($this->manager);! $builder->addModelTransformer($transformer);! }! ! public function getParent()! {! return 'text';! }! ! public function getName()! {! return 'my_text';! }! }!
  27. 27. Data transformers class MeetingType extends AbstractType! {! public function buildForm(FormBuilderInterface $builder, array $option)! {! $builder->add('name', 'my_text');! $builder->add('when', 'date');! $builder->add('featured', 'checkbox');! }! ! public function getName()! {! return 'meeting';! }! }! Andrea Giuliano @bit_shark use my_text as a standard type
  28. 28. Andrea Giuliano @bit_shark Remember Data Transformers transforms data representation Don’t use them to change data information
  29. 29. Events
  30. 30. Andrea Giuliano @bit_shark To change data information use form events Events
  31. 31. Events class FacebookSubscriber implements EventSubscriberInterface! {! public static function getSubscribedEvents()! {! return array(FormEvents::PRE_SET_DATA => 'preSetData');! }! ! public function preSetData(FormEvent $event)! {! $data = $event->getData();! $form = $event->getForm();! ! if (null === $data) {! Andrea Giuliano @bit_shark return;! }! ! if (!$data->getFacebookId()) {! $form->add('username');! $form->add('password');! }! }! }
  32. 32. Events class RegistrationType extends AbstractType! {! public function buildForm(FormBuilderInterface $builder, array $options)! {! $builder->add('name');! $builder->add('surname');! ! $builder->addEventSubscriber(new FacebookSubscriber());! }! ! public function getName()! {! return 'registration';! }! ! ...! ! } Andrea Giuliano @bit_shark add it to your type
  33. 33. Events class RegistrationForm extends AbstractType! {! public function buildForm(FormBuilderInterface $builder, array $options)! {! $builder->add('name');! $builder->add('surname');! ! $builder->get('surname')->addEventListener(! Andrea Giuliano @bit_shark FormEvents::BIND,! function(FormEvent $event){! $event->setData(ucwords($event->getData()))! }! );! }! ! public function getName()! {! return 'registration';! }! }
  34. 34. Events $form->setData($symfonyDay); PRE_SET_DATA Model Norm View Andrea Giuliano @bit_shark meeting [Form] POST_SET_DATA child
  35. 35. Events POST_BIND $form->handleRequest($request); Model Norm View Andrea Giuliano @bit_shark meeting [Form] child PRE_BIND BIND
  36. 36. Test
  37. 37. namespace AcmeTestBundleTestsFormType;! ! use DnseeEventBundleFormTypeEventType;! use DnseeEventBundleModelEventObject;! use SymfonyComponentFormTestTypeTestCase;! ! class MeetingTypeTest extends TypeTestCase! {! Andrea Giuliano @bit_shark public function testSubmitValidData()! {! $formData = array(! 'name' => 'SymfonyDay',! 'date' => '2013-10-18',! 'featured' => true,! );! ! $type = new TestedType();! $form = $this->factory->create($type);! ! $object = new TestObject();! $object->fromArray($formData);! ! // submit the data to the form directly! $form->submit($formData);! ! $this->assertTrue($form->isSynchronized());! $this->assertEquals($object, $form->getData());! ! $view = $form->createView();! $children = $view->children;! ! foreach (array_keys($formData) as $key) {! $this->assertArrayHasKey($key, $children);! }! }! } Test
  38. 38. namespace AcmeTestBundleTestsFormType;! ! use DnseeEventBundleFormTypeEventType;! use DnseeEventBundleModelEvent;! use SymfonyComponentFormTestTypeTestCase;! ! class MeetingTypeTest extends TypeTestCase! {! public function testSubmitValidData()! {! $formData = array(! Andrea Giuliano @bit_shark 'name' => 'SymfonyDay',! 'when' => '2013-10-18',! 'featured' => true,! );! ! $type = new EventType();! $form = $this->factory->create($type);! ! $event = new Event();! $event->fromArray($formData);! ! [...]! Test decide the data to be submitted
  39. 39. Test namespace AcmeTestBundleTestsFormType;! ! use DnseeEventBundleFormTypeEventType;! use DnseeEventBundleModelEventObject;! use SymfonyComponentFormTestTypeTestCase;! ! class MeetingTypeTest extends TypeTestCase! {! public function testSubmitValidData()! {! ! [...]! ! $form->submit($formData);! ! $this->assertTrue($form->isSynchronized());! $this->assertEquals($object, $form->getData());! ! [...]! ! Andrea Giuliano @bit_shark test data transformers
  40. 40. Test namespace AcmeTestBundleTestsFormType;! ! use AcmeTestBundleFormTypeTestedType;! use AcmeTestBundleModelTestObject;! use SymfonyComponentFormTestTypeTestCase;! ! class MeetingTypeTest extends TypeTestCase! {! public function testSubmitValidData()! {! ! [...]! ! $view = $form->createView();! $children = $view->children;! ! foreach (array_keys($formData) as $key) {! Andrea Giuliano @bit_shark $this->assertArrayHasKey($key, $children);! }! }! }! test form view creation
  41. 41. Test namespace AcmeTestBundleTestsFormType;! ! use DnseeEventBundleFormTypeEventType;! use DnseeEventBundleModelEventObject;! use SymfonyComponentFormTestTypeTestCase;! ! class MeetingTypeTest extends TypeTestCase! {! protected function getExtensions()! {! $myCustomType = new MyCustomType();! return array(new PreloadedExtension(array(! Andrea Giuliano @bit_shark $myCustomType->getName() => $customType,! ), array()));! }! ! public function testSubmitValidData()! {! [...]! }! }
  42. 42. Test namespace AcmeTestBundleTestsFormType;! ! use DnseeEventBundleFormTypeEventType;! use DnseeEventBundleModelEventObject;! use SymfonyComponentFormTestTypeTestCase;! ! class MeetingTypeTest extends TypeTestCase! {! protected function getExtensions()! {! $myCustomType = new MyCustomType();! return array(new PreloadedExtension(array(! Andrea Giuliano @bit_shark $myCustomType->getName() => $customType,! ), array()));! }! ! public function testSubmitValidData()! {! [...]! }! } Test your custom type FIRST
  43. 43. ?
  44. 44. http://joind.in/9784 Andrea Giuliano @bit_shark
  45. 45. References https://speakerdeck.com/webmozart/symfony2-form-tricks http://www.flickr.com/photos/yahya/132963781/ http://www.flickr.com/photos/lutherankorean/2694858251/ http://www.flickr.com/photos/lauroroger/8808985531/ http://www.flickr.com/photos/gifake/4643253235/ http://www.flickr.com/photos/zorin-denu/5222189908/ http://www.flickr.com/photos/aigle_dore/10014783623/ http://www.flickr.com/photos/skosoris/4985591296/ http://www.flickr.com/photos/sharynmorrow/248647126/

×