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

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

Upload: andrea-giuliano

Post on 06-May-2015

2.718 views

Category:

Technology


5 download

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

TRANSCRIPT

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/