Leveraging Symfony2 Forms

74
Bernhard Schussek  Leveraging Symfony2 Forms Symfony Live Conference, March 03 rd  2011

description

This session will introduce you to the new Form component in Symfony2. With the new domain-driven paradigma and its flexible design, the component opens a door to a wide range of possibilities. The brand new architecture makes creating complex forms easier and faster than ever before. This talk will teach you today what you need to know to build powerful forms tomorrow.

Transcript of Leveraging Symfony2 Forms

Page 1: Leveraging Symfony2 Forms

Bernhard Schussek 

Leveraging Symfony2 Forms 

Symfony Live Conference, March 03rd 2011

Page 2: Leveraging Symfony2 Forms

About myself

Software Architect in Vienna

Student of Software Engineering

Symfony since 2006

Outdoor and music junkie

Page 3: Leveraging Symfony2 Forms

Agenda

Introductory example

The Form Config class

Form processing

Fields

Useful features

Page 4: Leveraging Symfony2 Forms

The Symfony2 Form component

Page 5: Leveraging Symfony2 Forms

The Symfony2 Form component

Evolution of symfony1 sfForm

2.5 years development

Fixes most of its problems

Reusable widgets

Embedded forms

Page 6: Leveraging Symfony2 Forms

Why 2.5 years?

Page 7: Leveraging Symfony2 Forms

"It takes a long time to make somthing complicated simple, but if you do, it will work without problems for a long time."

– F. Andy Seidl, http://faseidl.com

Page 8: Leveraging Symfony2 Forms

Service Oriented Architecture

Applications provide services

Services are exchangable

These services can be consumed by different actors

Humans

Machines

Page 9: Leveraging Symfony2 Forms

Forms don't contain business logic

Page 10: Leveraging Symfony2 Forms

Services do

Page 11: Leveraging Symfony2 Forms

Example: Online sausage shop

Page 12: Leveraging Symfony2 Forms

Example: Online sausage shop

You

Request

Response

Page 13: Leveraging Symfony2 Forms

Example: Online sausage shop

Request

Response

YouConsumer Service

Page 14: Leveraging Symfony2 Forms

Example: Online sausage shop

Request

Response

Consumer Service

Page 15: Leveraging Symfony2 Forms

Example: Online sausage shop

Request

Response

Consumer Service

Page 16: Leveraging Symfony2 Forms

Service implementation

class Order{ function setName($name);

function setAddress($address);

function setSausage($sausage);

function setAmount($amount);}

Page 17: Leveraging Symfony2 Forms

Flow of information

I am Max!

Page 18: Leveraging Symfony2 Forms

Flow of information

I am Max!

$order->setName('Max')

Page 19: Leveraging Symfony2 Forms

Flow of information

Sensio Labs,Paris

Address:

MaxName:

BratwurstSausage:

5Amount:

Order form

Page 20: Leveraging Symfony2 Forms

Flow of information

Sensio Labs,Paris

Address:

MaxName:

BratwurstSausage:

5Amount:

$order->setName('Max')

->setAddress( 'Sensio Labs, Paris')

->setSausage( 'Bratwurst')->setAmount(5);

Order form

Page 21: Leveraging Symfony2 Forms

Flow of information

$order->getName()

->getAddress()

->getSausage()

->getAmount();

Liip OfficeZurich

Address:

BobName:

CervelatSausage:

10Amount:

Existing order

Page 22: Leveraging Symfony2 Forms

The Form Config class

Page 23: Leveraging Symfony2 Forms

class OrderFormConfig extends AbstractConfig{ public function configure( FieldInterface $form, array $options) {

}}

PHP class

Form definition

Page 24: Leveraging Symfony2 Forms

class OrderFormConfig extends AbstractConfig{ public function configure( FieldInterface $form, array $options) { $form->add('text', 'name') ->add('text', 'address') ->add('text', 'sausage') ->add('integer', 'amount'); }}

PHP class

Form definition

Page 25: Leveraging Symfony2 Forms

class OrderFormConfig extends AbstractConfig{

...

public function getIdentifier() { return 'form.order'; }}

PHP class

Form definition

Page 26: Leveraging Symfony2 Forms

Why not a simple OrderForm class?

Page 27: Leveraging Symfony2 Forms

Why we need Config classes

OrderForm cannot be put into the Dependency Injection Container ...

... but OrderFormConfig can!

Page 28: Leveraging Symfony2 Forms

Dependency Injection Container

<service id="form.order" class="OrderFormConfig"> <tag name="form.config" alias="form.order" /> <argument type="service" id="form.factory" /></service>

public function getIdentifier(){ return 'form.order';}

Tag alias == form identifier

Page 29: Leveraging Symfony2 Forms

Form processing

Page 30: Leveraging Symfony2 Forms

Form processing

$factory = $this->get('form.factory');$form = $factory->getInstance('form.order');

$form->setData($order);

if ($request->getMethod() === 'POST') { $form->bindRequest($request);

if ($form->isValid()) { $order->send(); return new RedirectResponse(...); }}

Form identifier

Page 31: Leveraging Symfony2 Forms

Form processing

$factory = $this->get('form.factory');$form = $factory->getInstance('form.order');

$form->setData($order);

if ($request->getMethod() === 'POST') { $form->bindRequest($request);

if ($form->isValid()) { $order->send(); return new RedirectResponse(...); }}

Service object

Page 32: Leveraging Symfony2 Forms

Form processing

$factory = $this->get('form.factory');$form = $factory->getInstance('form.order');

$form->setData($order);

if ($request->getMethod() === 'POST') { $form->bindRequest($request);

if ($form->isValid()) { $order->send(); return new RedirectResponse(...); }}

Calls setters

Page 33: Leveraging Symfony2 Forms

Form processing

$factory = $this->get('form.factory');$form = $factory->getInstance('form.order');

$form->setData($order);

if ($request->getMethod() === 'POST') { $form->bindRequest($request);

if ($form->isValid()) { $order->send(); return new RedirectResponse(...); }} $order now contains submitted data!

Page 34: Leveraging Symfony2 Forms

Form rendering

In the action

return $this->render( 'HelloBundle:Hello:index.twig.html', array('form' => $form->getRenderer()));

Contains view variables and methods

Page 35: Leveraging Symfony2 Forms

Form rendering

In the template

<form action="#" method="post"> {{ form.widget }}</form>

"widget" is a view method

Page 36: Leveraging Symfony2 Forms

Form rendering

<form action="#" method="post"> {{ form.errors }} {% for field in form.vars.fields %} {{ field.errors }} {{ field.label }} {{ field.widget }} {% endfor %} {{ form.rest }}</form>

"fields" is a view variable

Page 37: Leveraging Symfony2 Forms

Form rendering

<form action="#" method="post"> {{ form.errors }} {{ form.name.errors }} {{ form.name.label }} {{ form.name.widget }} {{ form.rest }}</form>

Page 38: Leveraging Symfony2 Forms

Form rendering

Renders all fields that weren't rendered before

... fields that you forgot to render manually

... hidden fields

{{ form.rest }}

Page 39: Leveraging Symfony2 Forms

What about validation?

Page 40: Leveraging Symfony2 Forms

Form validation

uses the Symfony2 Validator

"Constraints" are put onto your PHP classes (services, entities etc.)

Page 41: Leveraging Symfony2 Forms

Validation constraints

class Order{ /** * @check:NotNull * @check:AssertType("string") * @check:MaxLength(50, message= * "Long name, dude...") */ private $name;}

Page 42: Leveraging Symfony2 Forms

Fields

Page 43: Leveraging Symfony2 Forms

Text input

$form->add('text', 'title');

Title:

Page 44: Leveraging Symfony2 Forms

Textarea

$form->add('textarea', 'content');

Content:

Page 45: Leveraging Symfony2 Forms

Date selector

$form->add('date', 'publishAt');

Publish at:

Mmmh localized!

Page 46: Leveraging Symfony2 Forms

Country selector

$form->add('country', 'nationality');

Nationality:

Localized too! Yummy!Localized too! Yummy!

Page 47: Leveraging Symfony2 Forms

File upload

$form->add('file', 'profilePicture');

Profile picture:

Remembers uploaded files on errors!

Page 48: Leveraging Symfony2 Forms

Repeated input

$form->add('repeated', 'email');

Email:

Email (again):

Page 49: Leveraging Symfony2 Forms

Core fieldsbirthday

checkbox

choice

collection

country

date

datetime

entity

file

hidden

integer

language

locale

money

number

password

percent

repeated

textarea

text

timezone

url

Page 50: Leveraging Symfony2 Forms

Field architecture

Filters

Value transformers

Page 51: Leveraging Symfony2 Forms

Filters

modify a value

uni­directional

Example: FixUrlProtocolFilter

symfony-project.com http://symfony-project.com

Page 52: Leveraging Symfony2 Forms

Value Transformers

convert values between two representations

bi­directional

Example: DateTimeToArrayTransformer

array( 'year' => 2011, 'month' => 5, 'day' => 1,)

object(DateTime)

Page 53: Leveraging Symfony2 Forms

Example: Entity fieldThe user sees:

Entity field

Checkbox"0"

Checkbox"1"

Checkbox"2"

Checkbox"3"

Symfony sees:

Tag IDs

Page 54: Leveraging Symfony2 Forms

Checkbox"0"

Checkbox"1"

Checkbox"2"

Checkbox"3"

array("0" => "0", "1" => "1", "2" => "1", ...)

"0" "1" "0" "0"

Tag IDs

Page 55: Leveraging Symfony2 Forms

Checkbox"0"

Checkbox"1"

Checkbox"2"

Checkbox"3"

array("0" => "0", "1" => "1", "2" => "1", ...)

"0" "1" "0" "0"

Tag IDs

Page 56: Leveraging Symfony2 Forms

Checkbox"0"

Checkbox"1"

Checkbox"2"

Checkbox"3"

false true true false

array("0" => false, "1" => true, "2" => true, ...)

ArrayCollection($securityTag, $validatorTag)

Page 57: Leveraging Symfony2 Forms

Checkbox"0"

Checkbox"1"

Checkbox"2"

Checkbox"3"

false true true false

array("0" => false, "1" => true, "2" => true, ...)

ArrayCollection($securityTag, $validatorTag)

ChoicesToArrayTransformer

Page 58: Leveraging Symfony2 Forms

Checkbox"0"

Checkbox"1"

Checkbox"2"

Checkbox"3"

false true true false

array("0" => false, "1" => true, "2" => true, ...)

ArrayCollection($securityTag, $validatorTag)

ArrayToEntitiesTransformer

Page 59: Leveraging Symfony2 Forms

CSRF protection

Cross­Site Request Forgery

A form is submitted using the session of another person

All kinds of misuse

Built­in protection in Symfony2

Page 60: Leveraging Symfony2 Forms

CSRF protection

app/config/config.yml

framework: csrf_protection: enabled: true secret: 30665e19ef0010d5620553

Page 61: Leveraging Symfony2 Forms

Field creation

Manual

Automatic

Symfony2 looks at metadata of the domain class to "guess" the correct field type and settings

E.g. Validator metadata, Doctrine2 metadata

Page 62: Leveraging Symfony2 Forms

Manual field creation

public function configure( FieldInterface $form, array $options){ $form->add('entity', 'sausage', array( 'class' => 'Sausage', ));}

but Doctrine already knows, that "sausage" is a To­One relationship to the Sausage class!

Page 63: Leveraging Symfony2 Forms

Automatic field creation

public function configure( FieldInterface $form, array $options){ $form->setDataClass('Order');

$form->add('sausage');}

Page 64: Leveraging Symfony2 Forms

Automatic with options overriding

public function configure( FieldInterface $form, array $options){ $form->setDataClass('Order') ->add('sausage', array( 'required' => false, ));}

Page 65: Leveraging Symfony2 Forms

Embedded forms

Symfony2 allows to embed forms into another very easily

Fields and forms implement FieldInterface

"A form is a field"

Page 66: Leveraging Symfony2 Forms

Embedded to­one forms

public function configure( FieldInterface $form, array $options){ $form->add('form.sausage', 'sausage');}

Identifier of SausageFormConfig

Page 67: Leveraging Symfony2 Forms

Embedded to­many forms

public function configure( FieldInterface $form, array $options){ $form->add('collection', 'sausages', array( 'identifier' => 'form.sausage' ));}

Identifier of SausageFormConfig

Page 68: Leveraging Symfony2 Forms

Config options

class ConcealedFieldConfig extends Abstract..{ public function getDefaultOptions($options) { return array( 'concealed' => true, ); }}

Options for influencing the field's/form's creation

Page 69: Leveraging Symfony2 Forms

Config inheritance

class ConcealedFieldConfig extends Abstract..{ public function getParent(array $options) { return $options['concealed'] ? 'password' : 'text'; }}

Dynamic inheritance from other forms/fields

Page 70: Leveraging Symfony2 Forms

Form themes

Are normal Twig templates

Blocks for each field type

{% block textarea__widget %} <textarea {{ block('attributes') }}> {{ value }} </textarea>{% endblock textarea__widget %}

Page 71: Leveraging Symfony2 Forms

Form themes

Can specify widget, errors, label and row templates for specific field types

{% block textarea__row %} <tr><td colspan="2"> {{ this.errors }} {{ this.widget }} </td></tr>{% endblock textarea__row %}

Page 72: Leveraging Symfony2 Forms

Form themes

Core themes:

TwigBundle::div_layout.html.twig

TwigBundle::table_layout.html.twig

Configuration in the DI parameter "form.theme.template"

More flexible configuration options coming soon

Page 73: Leveraging Symfony2 Forms

Questions?

Thanks for listening!

Code can currently be found onhttps://github.com/bschussek/symfony/tree/experimental

Bernhard SchussekTwitter: @webmozart

Page 74: Leveraging Symfony2 Forms

The End

Copyright

"dog window" by Larry Wentzelhttp://www.flickr.com/photos/wentzelepsy

"Symfony Live 2011 Logo" by Sensio Labshttp://www.sensiolabs.com