Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Everything you always wanted to know about form...

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

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à.

Andrea Giuliano

October 19, 2013

Transcript

45 slides
  1. SYMFONYDAY 2013 Everything you always wanted to know about forms*

    *but were afraid to ask @bit_shark Andrea Giuliano
  2. 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 Tree: Abstract data Type
  3. Andrea Giuliano @bit_shark Let’s create it with Symfony namespace MyApp\MyBundle\Form;!

    ! use Symfony\Component\Form\AbstractType;! use Symfony\Component\Form\FormBuilderInterface;! use Symfony\Component\OptionsResolver\OptionsResolverInterface;! ! 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';! }! }
  4. 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
  5. 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
  6. Andrea Giuliano @bit_shark 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;! } Data format
  7. Andrea Giuliano @bit_shark How the information is represented in the

    application model Data format Model Data Norm Data View Data
  8. Andrea Giuliano @bit_shark Data format Model Data Norm Data View

    Data How the information is represented in the view domain
  9. Andrea Giuliano @bit_shark $builder->add('when', 'date') widget View Data choice array

    single_text string input Model Data string string datetime DateTime array array timestamp integer Meeting form Date date Model Data and View Data
  10. 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 Model Data and View Data What $form->getData() will return?
  11. Andrea Giuliano @bit_shark Which format would you like to play

    with in your application logic? $form->getNormData() Normalized Data
  12. Andrea Giuliano @bit_shark 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;! }! }
  13. Andrea Giuliano @bit_shark Model Data Norm Data View Data Model

    Data Norm Data View Data transform() transform() reverseTransform() reverseTransform() Model Transformer View Transformer Data transformers
  14. Andrea Giuliano @bit_shark 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
  15. Andrea Giuliano @bit_shark 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';! }! }! in case of dependencies?
  16. Andrea Giuliano @bit_shark Data transformers Use it by creating your

    own type <service id="dnsee.type.my_text" ! class="Dnsee\MyBundle\Form\Type\MyTextType">! <argument type="service" id="dnsee.my_awesome_manager"/>! <tag name="form.type" alias="my_text" />! </service>
  17. Andrea Giuliano @bit_shark Data transformers Use it by creating your

    own type class MyTextType extends AbstractType! {! private $myManager;! ! 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';! }! }!
  18. Andrea Giuliano @bit_shark 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';! }! }! use my_text as a standard type
  19. Andrea Giuliano @bit_shark 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) {! return;! }! ! if (!$data->getFacebookId()) {! $form->add('username');! $form->add('password');! }! }! } Events
  20. Andrea Giuliano @bit_shark 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';! }! ! ...! ! } Events add it to your type
  21. Andrea Giuliano @bit_shark class RegistrationForm extends AbstractType! {! public function

    buildForm(FormBuilderInterface $builder, array $options)! {! $builder->add('name');! $builder->add('surname');! ! $builder->get('surname')->addEventListener(! FormEvents::BIND,! function(FormEvent $event){! $event->setData(ucwords($event->getData()))! }! );! }! ! public function getName()! {! return 'registration';! }! } Events
  22. Andrea Giuliano @bit_shark POST_BIND Events Model Norm View meeting [Form]

    child $form->handleRequest($request); PRE_BIND BIND
  23. Andrea Giuliano @bit_shark namespace Acme\TestBundle\Tests\Form\Type;! ! use Dnsee\EventBundle\Form\Type\EventType;! use Dnsee\EventBundle\Model\EventObject;!

    use Symfony\Component\Form\Test\TypeTestCase;! ! class MeetingTypeTest extends TypeTestCase! {! 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
  24. Andrea Giuliano @bit_shark namespace Acme\TestBundle\Tests\Form\Type;! ! use Dnsee\EventBundle\Form\Type\EventType;! use Dnsee\EventBundle\Model\Event;!

    use Symfony\Component\Form\Test\TypeTestCase;! ! class MeetingTypeTest extends TypeTestCase! {! public function testSubmitValidData()! {! $formData = array(! '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
  25. Andrea Giuliano @bit_shark namespace Acme\TestBundle\Tests\Form\Type;! ! use Dnsee\EventBundle\Form\Type\EventType;! use Dnsee\EventBundle\Model\EventObject;!

    use Symfony\Component\Form\Test\TypeTestCase;! ! class MeetingTypeTest extends TypeTestCase! {! public function testSubmitValidData()! {! ! [...]! ! $form->submit($formData);! ! $this->assertTrue($form->isSynchronized());! $this->assertEquals($object, $form->getData());! ! [...]! ! Test test data transformers
  26. Andrea Giuliano @bit_shark namespace Acme\TestBundle\Tests\Form\Type;! ! use Acme\TestBundle\Form\Type\TestedType;! use Acme\TestBundle\Model\TestObject;!

    use Symfony\Component\Form\Test\TypeTestCase;! ! class MeetingTypeTest extends TypeTestCase! {! public function testSubmitValidData()! {! ! [...]! ! $view = $form->createView();! $children = $view->children;! ! foreach (array_keys($formData) as $key) {! $this->assertArrayHasKey($key, $children);! }! }! }! Test test form view creation
  27. Andrea Giuliano @bit_shark namespace Acme\TestBundle\Tests\Form\Type;! ! use Dnsee\EventBundle\Form\Type\EventType;! use Dnsee\EventBundle\Model\EventObject;!

    use Symfony\Component\Form\Test\TypeTestCase;! ! class MeetingTypeTest extends TypeTestCase! {! protected function getExtensions()! {! $myCustomType = new MyCustomType();! return array(new PreloadedExtension(array(! $myCustomType->getName() => $customType,! ), array()));! }! ! public function testSubmitValidData()! {! [...]! }! } Test
  28. Andrea Giuliano @bit_shark namespace Acme\TestBundle\Tests\Form\Type;! ! use Dnsee\EventBundle\Form\Type\EventType;! use Dnsee\EventBundle\Model\EventObject;!

    use Symfony\Component\Form\Test\TypeTestCase;! ! class MeetingTypeTest extends TypeTestCase! {! protected function getExtensions()! {! $myCustomType = new MyCustomType();! return array(new PreloadedExtension(array(! $myCustomType->getName() => $customType,! ), array()));! }! ! public function testSubmitValidData()! {! [...]! }! } Test Test your custom type FIRST
  29. ?

More Decks by Andrea Giuliano

Other Decks in Programming