0% found this document useful (0 votes)
46 views

Forms Symfony2

The document discusses Symfony forms and how they are represented as tree structures. It provides an example of creating a meeting form in Symfony, and explains how Symfony represents form data in different formats including model data, normalized data, and view data. It also discusses how data transformers can be used to transform data between these different formats.

Uploaded by

cquinto
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
46 views

Forms Symfony2

The document discusses Symfony forms and how they are represented as tree structures. It provides an example of creating a meeting form in Symfony, and explains how Symfony represents form data in different formats including model data, normalized data, and view data. It also discusses how data transformers can be used to transform data between these different formats.

Uploaded by

cquinto
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 45

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

collection of nodes each of which has an associated


value and a list of children connected to their parents by
means of an edge
Andrea Giuliano @bit_shark
Symfony Forms are trees

Form

Form Form Form

Form

Andrea Giuliano @bit_shark


Example: a meeting form

Andrea Giuliano @bit_shark


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

Andrea Giuliano @bit_shark


Symfonys point of view

Meeting
form

Andrea Giuliano @bit_shark


Symfonys point of view

Meeting
form

Name
string

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

Andrea Giuliano @bit_shark


Symfonys point of view

Meeting
form

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

Name Date
string date

Month Day Year


choice choice choice

Andrea Giuliano @bit_shark


Symfonys point of view

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

Meeting
form

Name Date Featured


string date checkbox

Month Day Year


choice choice choice

Andrea Giuliano @bit_shark


Data format
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


Data format

Model Norm View


Data Data Data

How the information



is represented in the application model

Andrea Giuliano @bit_shark


Data format

Model Norm View


Data Data Data

How the information



is represented in the view domain

Andrea Giuliano @bit_shark


Data format

Model Norm View


Data Data Data

???

Andrea Giuliano @bit_shark


Model Data and View Data

Meeting
widget View Data input Model Data
form

choice array string string


Date single_text string datetime DateTime
date

array array

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

Andrea Giuliano @bit_shark


Model Data and View Data

Meeting
widget View Data input Model Data
form

choice array string string


Date single_text string datetime DateTime
date

array array

timestamp integer

What $form->getData() will return?


Andrea Giuliano @bit_shark
Normalized Data

Which format would you like to play with


in your application logic?

$form->getNormData()

Andrea Giuliano @bit_shark


Data Transformers
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


Data transformers

transform() transform()

Model Norm View


Data Data Data
Model Transformer View Transformer

reverseTransform() reverseTransform()

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

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?

Andrea Giuliano @bit_shark


Andrea Giuliano @bit_shark
Data transformers

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

Use it by creating your own type


Andrea Giuliano @bit_shark
Data transformers
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';!
}!
}!

Use it by creating your own type


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


Andrea Giuliano @bit_shark
Remember

Data Transformers transforms


data representation

Dont use them to change


data information

Andrea Giuliano @bit_shark


Events
Events

To change data information use


form events

Andrea Giuliano @bit_shark


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) {!
return;!
}!
!
if (!$data->getFacebookId()) {!
$form->add('username');!
$form->add('password');!
}!
}!
}

Andrea Giuliano @bit_shark


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';! add it to your type
}!
!
...!
!
}

Andrea Giuliano @bit_shark


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

Andrea Giuliano @bit_shark


Events

$form->setData($symfonyDay);

PRE_SET_DATA

meeting [Form]

Model Norm View POST_SET_DATA

child

Andrea Giuliano @bit_shark


Events
$form->handleRequest($request);

POST_BIND
PRE_BIND

child

meeting [Form]

BIND

Model Norm View

Andrea Giuliano @bit_shark


Test
Test
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);!
}!
}!
}

Andrea Giuliano @bit_shark


Test
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()!
{! decide the data
$formData = array(! to be submitted
'name' => 'SymfonyDay',!
'when' => '2013-10-18',!
'featured' => true,!
);!
!
$type = new EventType();!
$form = $this->factory->create($type);!
!
$event = new Event();!
$event->fromArray($formData);!
!
[...]!

Andrea Giuliano @bit_shark


Test
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()!
{!
! test data transformers
[...]!
!
$form->submit($formData);!
!
$this->assertTrue($form->isSynchronized());!
$this->assertEquals($object, $form->getData());!
!
[...]!
!

Andrea Giuliano @bit_shark


Test

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()!
{!
!
[...]!
test form view creation
!
$view = $form->createView();!
$children = $view->children;!
!
foreach (array_keys($formData) as $key) {!
$this->assertArrayHasKey($key, $children);!
}!
}!
}!

Andrea Giuliano @bit_shark


Test
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()!
{!
[...]!
}!
}

Andrea Giuliano @bit_shark


Test
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 your custom type
[...]! FIRST
}!
}

Andrea Giuliano @bit_shark


?
http://joind.in/9784

Andrea Giuliano
@bit_shark
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/

You might also like