45
SYMFONYDAY 2013 Everything you always wanted to know about forms* *but were afraid to ask @bit_shark Andrea Giuliano

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

Embed Size (px)

DESCRIPTION

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

Citation preview

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

SYMFONYDAY 2013

Everything you always wanted to know about forms*

*but were afraid to ask

@bit_sharkAndrea Giuliano

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

TreeAbstract Data Structure

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

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

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

Andrea Giuliano @bit_shark

Symfony Forms are trees

Form

Form Form Form

Form

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

Andrea Giuliano @bit_shark

Example: a meeting form

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

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';! }!}

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

Andrea Giuliano @bit_shark

Meeting

form

Symfony’s point of view

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

Andrea Giuliano @bit_shark

$builder->add('name', 'string')

Meeting

form

Namestring

Symfony’s point of view

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

Andrea Giuliano @bit_shark

$builder->add('when', 'date')

Meeting

form

Namestring

Datedate

Month

choice

Day

choice

Year

choice

Symfony’s point of view

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

Andrea Giuliano @bit_shark

Meeting

form

Namestring

Datedate

Month

choice

Day

choice

Year

choice

Symfony’s point of view

$builder->add('featured', 'checkbox')

Featuredcheckbox

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

Data format

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

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

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

Andrea Giuliano @bit_shark

How the information is represented in the application model

Data format

Model Data

Norm Data

View Data

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

Andrea Giuliano @bit_shark

Data format

Model Data

Norm Data

View Data

How the information is represented in the view domain

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

Andrea Giuliano @bit_shark

Data format

Model Data

Norm Data

View Data

???

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

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

Datedate

Model Data and View Data

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

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

Datedate

Model Data and View Data

What $form->getData() will return?

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

Andrea Giuliano @bit_shark

Which format would you like to play with in your application logic?

$form->getNormData()

Normalized Data

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

Data Transformers

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

Andrea Giuliano @bit_shark

Data transformersclass 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;! }!}

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

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

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

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

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

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?

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

Andrea Giuliano @bit_shark

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

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>

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

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';! }!}!

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

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

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

Andrea Giuliano @bit_shark

Remember

Data Transformers transforms data representation

Don’t use them to change data information

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

Events

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

Andrea Giuliano @bit_shark

To change data information use form events

Events

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

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

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

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

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

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

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

Andrea Giuliano @bit_shark

PRE_SET_DATA

Events

Model Norm View

$form->setData($symfonyDay);

meeting [Form]

POST_SET_DATA

child

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

Andrea Giuliano @bit_shark

POST_BIND

Events

Model Norm View

meeting [Form]

child

$form->handleRequest($request);

PRE_BIND

BIND

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

Test

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

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

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

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

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

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

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

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

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

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

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

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

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

?

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

Andrea Giuliano@bit_shark

http://joind.in/9784

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

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/