Download - Leveraging Symfony2 Forms

Transcript
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