Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

About myself Software Architect in Vienna Student of Software Engineering Symfony since 2006 Outdoor and music junkie

Slide 3

Slide 3 text

Agenda Introductory example The Form Config class Form processing Fields Useful features

Slide 4

Slide 4 text

The Symfony2 Form component

Slide 5

Slide 5 text

The Symfony2 Form component Evolution of symfony1 sfForm 2.5 years development Fixes most of its problems Reusable widgets Embedded forms

Slide 6

Slide 6 text

Why 2.5 years?

Slide 7

Slide 7 text

"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

Slide 8

Slide 8 text

Service Oriented Architecture Applications provide services Services are exchangable These services can be consumed by different actors Humans Machines

Slide 9

Slide 9 text

Forms don't contain business logic

Slide 10

Slide 10 text

Services do

Slide 11

Slide 11 text

Example: Online sausage shop

Slide 12

Slide 12 text

Example: Online sausage shop You Request Response

Slide 13

Slide 13 text

Example: Online sausage shop Request Response You Consumer Service

Slide 14

Slide 14 text

Example: Online sausage shop Request Response Consumer Service

Slide 15

Slide 15 text

Example: Online sausage shop Request Response Consumer Service

Slide 16

Slide 16 text

Service implementation class Order { function setName($name); function setAddress($address); function setSausage($sausage); function setAmount($amount); }

Slide 17

Slide 17 text

Flow of information I am Max!

Slide 18

Slide 18 text

Flow of information I am Max! $order->setName('Max')

Slide 19

Slide 19 text

Flow of information Sensio Labs, Paris Address: Max Name: Bratwurst Sausage: 5 Amount: Order form

Slide 20

Slide 20 text

Flow of information Sensio Labs, Paris Address: Max Name: Bratwurst Sausage: 5 Amount: $order ->setName('Max') ->setAddress( 'Sensio Labs, Paris') ->setSausage( 'Bratwurst') ->setAmount(5); Order form

Slide 21

Slide 21 text

Flow of information $order ->getName() ->getAddress() ->getSausage() ->getAmount(); Liip Office Zurich Address: Bob Name: Cervelat Sausage: 10 Amount: Existing order

Slide 22

Slide 22 text

The Form Config class

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

class OrderFormConfig extends AbstractConfig { ... public function getIdentifier() { return 'form.order'; } } PHP class Form definition

Slide 26

Slide 26 text

Why not a simple OrderForm class?

Slide 27

Slide 27 text

Why we need Config classes OrderForm cannot be put into the Dependency Injection Container ... ... but OrderFormConfig can!

Slide 28

Slide 28 text

Dependency Injection Container public function getIdentifier() { return 'form.order'; } Tag alias == form identifier

Slide 29

Slide 29 text

Form processing

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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!

Slide 34

Slide 34 text

Form rendering In the action return $this->render( 'HelloBundle:Hello:index.twig.html', array('form' => $form->getRenderer())); Contains view variables and methods

Slide 35

Slide 35 text

Form rendering In the template {{ form.widget }} "widget" is a view method

Slide 36

Slide 36 text

Form rendering {{ form.errors }} {% for field in form.vars.fields %} {{ field.errors }} {{ field.label }} {{ field.widget }} {% endfor %} {{ form.rest }} "fields" is a view variable

Slide 37

Slide 37 text

Form rendering {{ form.errors }} {{ form.name.errors }} {{ form.name.label }} {{ form.name.widget }} {{ form.rest }}

Slide 38

Slide 38 text

Form rendering Renders all fields that weren't rendered before ... fields that you forgot to render manually ... hidden fields {{ form.rest }}

Slide 39

Slide 39 text

What about validation?

Slide 40

Slide 40 text

Form validation uses the Symfony2 Validator "Constraints" are put onto your PHP classes (services, entities etc.)

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

Fields

Slide 43

Slide 43 text

Text input $form->add('text', 'title'); Title:

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

Date selector $form->add('date', 'publishAt'); Publish at: Mmmh localized!

Slide 46

Slide 46 text

Country selector $form->add('country', 'nationality'); Nationality: Localized too! Yummy! Localized too! Yummy!

Slide 47

Slide 47 text

File upload $form->add('file', 'profilePicture'); Profile picture: Remembers uploaded files on errors!

Slide 48

Slide 48 text

Repeated input $form->add('repeated', 'email'); Email: Email (again):

Slide 49

Slide 49 text

Core fields birthday checkbox choice collection country date datetime entity file hidden integer language locale money number password percent repeated textarea text timezone url

Slide 50

Slide 50 text

Field architecture Filters Value transformers

Slide 51

Slide 51 text

Filters modify a value uni­directional Example: FixUrlProtocolFilter symfony-project.com http://symfony-project.com

Slide 52

Slide 52 text

Value Transformers convert values between two representations bi­directional Example: DateTimeToArrayTransformer array( 'year' => 2011, 'month' => 5, 'day' => 1, ) object(DateTime)

Slide 53

Slide 53 text

Example: Entity field The user sees: Entity field Checkbox "0" Checkbox "1" Checkbox "2" Checkbox "3" Symfony sees: Tag IDs

Slide 54

Slide 54 text

Checkbox "0" Checkbox "1" Checkbox "2" Checkbox "3" array("0" => "0", "1" => "1", "2" => "1", ...) "0" "1" "0" "0" Tag IDs

Slide 55

Slide 55 text

Checkbox "0" Checkbox "1" Checkbox "2" Checkbox "3" array("0" => "0", "1" => "1", "2" => "1", ...) "0" "1" "0" "0" Tag IDs

Slide 56

Slide 56 text

Checkbox "0" Checkbox "1" Checkbox "2" Checkbox "3" false true true false array("0" => false, "1" => true, "2" => true, ...) ArrayCollection($securityTag, $validatorTag)

Slide 57

Slide 57 text

Checkbox "0" Checkbox "1" Checkbox "2" Checkbox "3" false true true false array("0" => false, "1" => true, "2" => true, ...) ArrayCollection($securityTag, $validatorTag) ChoicesToArrayTransformer

Slide 58

Slide 58 text

Checkbox "0" Checkbox "1" Checkbox "2" Checkbox "3" false true true false array("0" => false, "1" => true, "2" => true, ...) ArrayCollection($securityTag, $validatorTag) ArrayToEntitiesTransformer

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

CSRF protection app/config/config.yml framework: csrf_protection: enabled: true secret: 30665e19ef0010d5620553

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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!

Slide 63

Slide 63 text

Automatic field creation public function configure( FieldInterface $form, array $options) { $form->setDataClass('Order'); $form->add('sausage'); }

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

Embedded forms Symfony2 allows to embed forms into another very easily Fields and forms implement FieldInterface "A form is a field"

Slide 66

Slide 66 text

Embedded to­one forms public function configure( FieldInterface $form, array $options) { $form->add('form.sausage', 'sausage'); } Identifier of SausageFormConfig

Slide 67

Slide 67 text

Embedded to­many forms public function configure( FieldInterface $form, array $options) { $form->add('collection', 'sausages', array( 'identifier' => 'form.sausage' )); } Identifier of SausageFormConfig

Slide 68

Slide 68 text

Config options class ConcealedFieldConfig extends Abstract.. { public function getDefaultOptions($options) { return array( 'concealed' => true, ); } } Options for influencing the field's/form's creation

Slide 69

Slide 69 text

Config inheritance class ConcealedFieldConfig extends Abstract.. { public function getParent(array $options) { return $options['concealed'] ? 'password' : 'text'; } } Dynamic inheritance from other forms/fields

Slide 70

Slide 70 text

Form themes Are normal Twig templates Blocks for each field type {% block textarea__widget %} {{ value }} {% endblock textarea__widget %}

Slide 71

Slide 71 text

Form themes Can specify widget, errors, label and row templates for specific field types {% block textarea__row %} {{ this.errors }} {{ this.widget }} {% endblock textarea__row %}

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

Questions? Thanks for listening! Code can currently be found on https://github.com/bschussek/symfony/tree/experimental Bernhard Schussek Twitter: @webmozart

Slide 74

Slide 74 text

The End Copyright "dog window" by Larry Wentzel http://www.flickr.com/photos/wentzelepsy "Symfony Live 2011 Logo" by Sensio Labs http://www.sensiolabs.com