symfony: Simplify your professional web development with PHP (IPC Frankfurt 2007)

Preview:

Citation preview

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

symfonySimplify your professional

web development with PHP

Fabien Potencierhttp://www.symfony-project.com/

http://www.sensiolabs.com/

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Sensio• French Web Agency, founded in 1998

– 150 people– 30 people dedicated to Web technologies

Webmarketing

Open SourceTechnologies

(Framework PHP)

WebTechnologies

SENSIOWeb Agency

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Sensio Labs• Open-Source technologies

– Linux– Apache– MySQL / PostgreSQL– PHP / Perl / Python

• Open-Source dedicated team• Big company customers

– Web Consulting– Audit / Training– Web Development

symfony PHP Framework

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

The symfony Framework• PHP 5 Web Framework• Based on 9 years of Sensio experience• Based on well-known projets (Mojavi, Propel, Prado)

• Open-Source• Built for :

– Professional Websites– Complex needs– Demanding environments Licence

MIT

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

The symfony Goals

• Bring together the « Entreprise World »and the Open-Source World

• Develop Faster

• Don’t Reinvent the Wheel

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Develop Faster• Each line of code has a cost

– To write the line– To test it– To maintain it

• Write less code– Architecture : controller, ORM, …– Configuration– Autoloading– Generators– Helpers

• More time for business rules, edge cases, …

less code

less complexity

less bugs

more productivity

more time

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Don’t Reinvent the Wheel• Follow best practices• MVC Pattern : Model / View / Controller

• Unit and functional test framework

• Environment and deployment support• Configurability• Security (XSS and CSRF protection by default)• Extensible (plugin system)• Admin Generator Simplify

your Dev. Life

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Main Selling Points• symfony is about good code but also …

• Documentation– GFDL book (450p)– The askeet tutorial (250p)

• 1.0 maintained for a long time– 1 release a month (only bug fixes)– Commercial support

1.0

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Let’s see some features

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Controller# apps/frontend/modules/blog/actions/actions.class.phpclass blogActions extends sfActions{ function executeShow() { $id = $this->getRequestParameter('id'); $this->post = PostPeer::retrieveByPk($id);

$this->forward404Unless($this->post); }}

Shortcut

Controller blog/show

For the View

Model call (Propel)

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Controllerclass blogActions extends sfActions{ function executeShow() { $this->forward404Unless( $this->post = PostPeer::retrieveByPk($this->getRequestParameter('id')) ); }}

class blogActions extends sfActions{ function showAction($request) { $this->forward404Unless( $this->post = PostPeer::retrieveByPk($request->getParameter('id')) ); }}

1.0

1.1

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Controller (Doctrine)# apps/frontend/modules/blog/actions/actions.class.phpclass blogActions extends sfActions{ function executeShow() { $id = $this->getRequestParameter('id'); $this->post = Doctrine::getTable('Post')->find($id);

$this->forward404Unless($this->post); }}

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Model (Propel)// lib/model/Author.phpclass Author extends BaseAuthor{ function getFullName() { return $this->getFirstName().' '.$this->getLastName(); }}

$author = new Author();$author->setFirstName('Fabien');$author->setLastName('Potencier');$author->save();

$post = new Post();$post->setAuthor($author);$post->setPublishedOn('tomorrow 12:00');$post->isPublished(true);$post->save();

$posts = PostPeer::doSelect(new Criteria());

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Model (Doctrine)// lib/model/doctrine/lib/Author.phpclass Author extends BaseAuthor{ function getFullName() { return $this->getFirstName().' '.$this->getLastName(); }}

$author = new Author();$author->setFirstName('Fabien');$author->setLastName('Potencier');$author->save();

$post = new Post();$post->setAuthor($author);$post->setPublishedOn('tomorrow 12:00');$post->isPublished(true);$post->save();

$posts = Doctrine::getTable('Post')->findAll();

$post = Doctrine::getTable('Post')->find($request->getParameter('id'));

Same as in Propel

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

View / Template# apps/frontend/modules/post/templates/showSuccess.php<h1 class="title"> <?php echo $post->getTitle() ?></h1>

<h2> par <?php echo $post->getAuthor()->getFullName() ?></h2>

<p> <?php echo $post->getHtmlContent(ESC_RAW) ?></p>

Raw Value

Escaped Escaped

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Routing

link_to( $post->getTitle(), '@post?id='.$post->getId())

post: param: { module: blog, action: show } requirements: id: \d+ url: /blog/:id

url_for('@homepage')homepage: param: { module: blog, action: recent } url: /

/blog/1

/

homepage: param: { module: blog, action: list } url: /recent: param: { module: blog, action: recent } url: /recent

/

/recent

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Routing in a Template<?php echo link_to($post->getTitle(), '@post?id='.$post->getId()) ?>

<a href="<?php echo url_for('@post?id='.$post->getId()) ?>">Next Post</a>

<?php echo link_to('Google', 'http://www.google.com/') ?>

Be pragmatic

<a href="http://www.google.com/">Google</a>

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Tests: Database Fixtures• Test data # data/fixtures/data.yml Author: fabien: first_name: Fabien last_name: Potencier Post: first_post: author_id: fabien title: IPC 2007 Conference

$ ./symfony propel-load-data frontend

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Functional Tests• Navigation simulation // test/functional/frontend/blogActionsTest.php $browser = new sfTestBrowser(); $browser->initialize(); $browser-> get('/blog/1')-> isStatusCode(200)-> checkResponseElement('h1.title', '/IPC 2007 Conference/');

TDDTest Driven Development

The power of CSS selectors

FluentInterface

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Be Happy… or not$ ./symfony test-functional frontend# get /ok 1 - status code is 200not ok 2 - response selector h1 does not match regex /IPC 2007 Conference/1..2 Looks like you failed 1 tests of 2.

$ ./symfony test-functional frontend# get /ok 1 - status code is 200ok 2 - response selector h1 matches regex /IPC 2007 Conference/1..2 Looks like everything went fine.

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Functional Tests# simple CSS selectorscheckResponseElement('h1.title', '/IPC 2007 Conference/')checkResponseElement('#title', '/IPC 2007 Conference/')

# attribute selectorscheckResponseElement('ul li a[class~="title"]', '/IPC 2007 Conference/')

# combinators: > and +checkResponseElement('ul > li', '/IPC 2007 Conference/')

# some CSS3 selectorscheckResponseElement('#list li:first-child', '/IPC 2007 Conference/')checkResponseElement('#list li:nth-child(3)', '/IPC 2007 Conference/')checkResponseElement('#list li:contains("IPC 2007 Conference")')

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Functional Testsclass ApplicationBrowser extends sfTestBrowser{ public function signin($user, $password) { return $this-> post('/signin', array('username' => $user, 'password' => $password))-> isRedirect()-> followRedirect()-> checkResponseElement('div.username', 'Welcome back Fabien'); }

public function signout() { return $this->get('/signout'); }}

DSLYour own specific browser

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Admin Generator: Backend Creation• Automatic generation of an Administration

Backend, ready for production usage– Lists– Pagination– Sorting

$ ./symfony propel-init-admin frontend post Post

1) Creates a post module2) Generates configuration

– Filters– Validation– CRUD

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Admin Generator• Generated code is MVC and customizable

– Configuration file (generator.yml)– Extend the Controller– Override some Templates / Partials

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Admin Generator: Configurability# apps/frontend/modules/post/config/generator.yml generator: class: sfPropelAdminGenerator param: model_class: Post list: display: [=title, author, created_at] filters: [title, author_id, published_on] max_per_page: 5

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Admin Generator• List

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Admin Generator• Edition

__toString()

widgets m2m relationship

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Admin Generator: Extensibility• Module extension class postActions extends autoPostActions { protected function addFiltersCriteria($c) { parent::addFiltersCriteria($c); $c->add(PostPeer::IS_PUBLISHED, true); } }

• Template customization

Generatedmodule

_edit_* : actions, footer, form, header, messages_list_* : footer, header, messages, td_actions, t(d|h)_stacked, t(d|h)_tabular_filters, editSuccess, listSuccess

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Debugging Tools• Web Debug Toolbar

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Debugging Tools• Error messages

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

What’s New in symfony 1.1?• A new task framework• Decoupling

– Remove singletons– Remove dependencies between core classes– New Event Dispatcher system

• Form / Validation framework

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

New Task Framework• Easier to extend the symfony tasks• Task namespaces• Built-in help system• Tasks are decoupled from the CLI

– Can be called from the CLI– … or from your own code… easily

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Task Namespacesgenerate :app Generates a new application (init-app) :batch Generates a new batch (init-batch) :controller Generates a new controller (init-controller) :module Generates a new module (init-module) :project Generates a new project (init-project)

test :all Launches all tests :functional Launches functional tests :unit Launches unit tests

i18n :extract Extracts i18n strings from php files :find Finds non "i18n ready" strings in an application

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Task Help$ ./symfony help plugin:install

Usage: symfony plugin:install [--stability|-s="..."] [--release|-r="..."] [--channel|-

c="..."] [--install_deps|-d] name

Aliases: plugin-install

Arguments: name The plugin name

Options: --stability (-s) The preferred stability (stable, beta, alpha) --release (-r) The preferred version --channel (-c) The PEAR channel name --install_deps (-d) Whether to force installation of required dependencies

Description: …

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Task HelpDescription: The plugin:install task installs a plugin:

./symfony plugin:install sfGuargPlugin

By default, it installs the latest stable release.

If you want to install a plugin that is not stable yet, use the stability option:

./symfony plugin:install --stability=beta sfGuargPlugin ./symfony plugin:install -s beta sfGuargPlugin

You can also force the installation of a specific version:

./symfony plugin:install --release=1.0.0 sfGuargPlugin ./symfony plugin:install -r 1.0.0 sfGuargPlugin

To force installation of all required dependencies, use the install_deps flag:

./symfony plugin:install --install-deps sfGuargPlugin ./symfony plugin:install -d sfGuargPlugin

By default, the PEAR channel used is symfony-plugins (plugins.symfony-project.org).

You can specify another channel with the channel option:

./symfony plugin:install --channel=mypearchannel sfGuargPlugin ./symfony plugin:install -c mypearchannel sfGuargPlugin…

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Task Calls in your Code# Somewhere in your code

$task = new sfCacheClearTask($dispatcher);$task->run();

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

symfony 1.0 Dependencies

CleanupDependencies

sfRequest

sfResponse

sfContext

sfUsersfStorage

sfRouting

sfI18NsfLogger

sfView

singleton dependency

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

symfony 1.1 Dependencies

CleanupDependencies

sfRequestsfResponse

sfContext

sfUser

sfStorage

sfRouting

sfI18N

sfLogger

sfEventDispatcher

sfView

singleton dependency

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfEventDispatcher• Based on Cocoa Notification Center// sfUser$event = new sfEvent($this, 'user.change_culture', array('culture' => $culture));$dispatcher->notify($event);

// sfI18N$callback = array($this, 'listenToChangeCultureEvent');$dispatcher->connect('user.change_culture', $callback);

• sfI18N and sfUser are decoupled• « Anybody » can listen to any event• You can notify existing events or create new ones

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Form & Validation Framework• Not a refactoring• A different approach• Almost no shared code• Symfony 1.1 can still use the old system

– set compat_10: on in settings.yml (off by default)– you can use the new and the old system in the same

application– symfony 1.0 sfValidator class has been renamed to

sfValidatorBase to avoid class name conflicts

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

The New Approach• Form as a first class object• Widget classes for form helpers• Validators validate arrays (it doesn’t care if it

comes from a request, an XML file or a modelobject)

• A decoupled system– Can be used without any other symfony class– 3 differents sub-systems

• Validators: can be used by themselves• Widgets: can be used by themselves• Form: glue between validators and widgets

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Validators• Every validator extends sfValidator• A validator cleans and validates input values• sfValidator provides some common features

– required (validation) > true by default– trim (cleaning) > false by default

• Each validator can have options:– sfValidatorString: max_length, min_length

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Validators// create a new string validator$v = new sfValidatorString(array('min_length' => 4));

// clean and validate data$value = $v->clean('Fabien');

// returns the input value$value == 'Fabien'

// change some option$v->setOption('trim', true);

$value = $v->clean(' Fabien ');

// trims the input value$value == 'Fabien' Validator objects

are stateless

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Custom Validatorclass CustomValidator extends sfValidator{ protected function configure($options = array(), $messages = array()) { $this->setOption('min_length', null); $this->setMessage('min_length', 'Too short.'); }

protected function doClean($value) { if (strlen($value) < $this->getOption('min_length')) { throw sfValidatorError($this, 'min_length', array('value' => $value)); }

return $value; }}

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Validators$v = new sfValidatorString(array('min_length' => 4));

// throws a sfValidatorError$v->clean('Jon');

$e->getCode() == 'min_length'$e->getMessage() == '"Jon" is too short (4 characters min).'

$v->setMessage('min_length', 'Too short (%name%).');$e->getMessage() == 'Too short (Jon).'

Use error codesor error messages

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Errors Internationalization$v = new sfValidatorString(array('min_length' => 4));

try{ $value = $v->clean('Jon');}catch (sfValidatorError $e){ echo $i18n->__($e->getMessageFormat(), $e->getArguments())."\n"; // or echo $i18n->__($e->getCode(), $e->getArguments())."\n";}

Error messagesare i18n ready

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfValidator(All|Any)• You can aggregate validators to create a new validator:

– sfValidatorAll: All validators must pass– sfValidatorAny: At least one validator must pass

$v1 = new sfValidatorString(array('min_length' => 4));$v2 = new sfValidatorString(array('max_length' => 10));

$v = new sfValidatorAll(array($v1, $v2));

// values must validate both validators$v->clean('Fabien');

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfValidator(All|Any)• As they are validators themselves, you can create

complex logic to validate your values:

// v1 && (v2 || v3) $v = new sfValidatorAll( array($v1, sfValidatorAny($v2, $v3)) );

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfValidatorSchema• A validator schema is composed of fields• A field is a named validator

$v1 = new sfValidatorString(array('min_length' => 4));

$v = new sfValidatorSchema(array( 'first_name' => $v1, 'last_name' => $v1, ));

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfValidatorSchema• The clean() method takes an array of named

values and returns an array:

$v->clean(array( 'first_name' => 'Fabien', 'last_name' => 'Potencier', ));

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfValidatorSchema

• It collects all errors for all fields• It throws an exception with all errors

sfValidatorErrorSchema: first_name: "Jon" is too short (4 characters min). last_name: "Jon" is too short (4 characters min). in ….php on line …

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfValidatorSchema• A named validator does not have access to the

whole array of inputs, just its named value• Two special validators:

– _pre_validator and _post_validator– They takes the array of values as input– They throw « global » errors

Isolation

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfValidatorSchema$v2 = new sfValidatorSchemaCompare( 'password', '==', 'password_bis');

$v = new sfValidatorSchema(array( 'password' => $v1, 'password_bis' => $v1,

'_post_validator' => $v2,));

sfValidatorErrorSchema:   "pass" does not match "word". in ….php on line …

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfValidatorSchema• sfValidatorSchema is a sfValidator, so you can nest them

$authorValidator = new sfValidatorSchema(array( 'first_name' => $v1, 'last_name' => $v1,));

$bookValidator = new sfValidatorSchema(array( 'title' => $v1, 'sub_title' => $v1, 'author' => $authorValidator,));

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfValidatorSchema$bookValidator->clean(array( 'title' => 'The symfony book', 'sub_title' => 'The definitive guide', 'author' => array( 'first_name' => 'Fabien', 'last_name' => 'Potencier', ),));

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfValidatorSchema• Secure by default

– allow_extra_fields (false by default)– filter_extra_fields (true by default)

• If you pass a value with no matching validator, sfValidatorSchemawill throw an error

• If you switch allow_extra_fields to true, then extra fields won’ttrigger an error but will be removed from the cleaned values

• If you also switch filter_extra_fields to false, then extra fields won’tbe removed from the cleaned values

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfValidatorSchemaForEach$choiceValidator = new sfValidatorSchema(array( 'choice' => $v1,));

$choicesValidator = new sfValidatorSchemaForEach($choiceValidator, 3);

$pollValidator = new sfValidatorSchema(array( 'question' => $v1, 'choices' => $choicesValidator,));

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfValidatorSchemaForEach$pollValidator->clean(array( 'question' => 'Do you like symfony?', 'choices' => array( array('choice' => 'Yes'), array('choice' => 'This is the best'), array('choice' => 'A lot'), ),));

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Validators as Strings$postValidator = new sfValidatorOr(array( new sfValidatorSchemaFilter('age', new sfValidatorInteger(array('min' => 18))), new sfValidatorAll(array( new sfValidatorSchemaFilter('age', new sfValidatorInteger(array('max' => 18))), new sfValidatorSchemaFilter('is_young', new sfValidatorBoolean(array('required' => true))), )),));

$string = $postValidator->asString();' age:Integer({min: 18}) or age:Integer({max: 18}) and is_young:Boolean({required: true})'

$postValidator = new sfValidatorFromDescription($string);

$postValidator->asPhp();

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Available Validators• Boolean• Choice• ChoiceMany• Date• DateTime• Email• Integer• Number• Regex• String• Time• Url

• All• Any• Callback• Decorator• Pass• FromDescription

• Schema• SchemaForEach• SchemaCompare• SchemaFilter

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Widgets• Every widget extends sfWidget• A widget is an object that can be rendered as an HTML

string• sfWidget provides some common features

– renderTag()– renderContentTag()– Charset support– XHTML or HTML closing tags

• Each widget can have HTML attributes:– Takes care of escaping– Fixes double escaping problems

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfWidgetForm• Base class for all form widgets• Some more properties like isHidden()• Generates an id if none provided and the widget

has a name– Default id is the widget name– Can be customized

$w = new sfWidgetFormInput(); $w->setIdFormat('id_%s');

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfWidgetForm classes// create a new input widget$w = new sfWidgetFormInput();

// render widgetecho $w->render('first_name', 'Fabien');

// returns the widget as HTML<input type="text" name="first_name" value="Fabien" id="first_name" />

// change some attributes$w->setAttribute('class', 'foo');

<input … class="foo" /> Widget objectsare stateless

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfWidgetForm classes// add default HTML attributes$w = new sfWidgetFormInput(array('class' => 'foo'));

// render widget with some HTML attributesecho $w->render('first_name', 'Fabien', array('class' => 'foo'));

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfWidgetFormSchema• A widget schema is composed of fields• A field is a named widget

$w1 = new sfWidgetFormInput();

$w = new sfWidgetFormSchema(array( 'first_name' => $w1, 'last_name' => $w1, ));

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfWidgetFormSchema• The render() method takes an array of named

values and returns an HTML string:

$w->render(null, array( 'first_name' => 'Fabien', 'last_name' => 'Potencier', ));

• You can also render individual fields

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfWidgetFormSchema• sfWidgetFormSchema is a sfWidget, so you can nest

them

$authorWidget = new sfWidgetFormSchema(array( 'first_name' => $w1, 'last_name' => $w1,));

$bookWidget = new sfWidgetFormSchema(array( 'title' => $w1, 'sub_title' => $w1, 'author' => $authorValidator,));

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfWidgetFormSchema$bookWidget->render(null, array( 'title' => 'The symfony book', 'sub_title' => 'The definitive guide', 'author' => array( 'first_name' => 'Fabien', 'last_name' => 'Potencier', ),));

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfWidgetFormSchema• The render() method can also render

– Errors– Nested form schema

• The render() method uses a formatter class– sfWidgetFormSchemaFormatterList– sfWidgetFormSchemaFormatterTable– Or build your own

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Forms• A sfForm is the glue between

– A validator schema– A widget schema

$validator = new sfValidatorSchema();$widget = new sfWidgetFormSchema();

$form = new sfForm();$form->setValidatorSchema($validator);$form->setWidgetSchema($widget);

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfForm• You can also create your own form class

class BookForm extends sfForm{ public function configure() { $validator = new BookValidatorSchema(); $widget = new BookWidgetFormSchema();

$this->setValidatorSchema($validator); $this->setWidgetSchema($widget); }}

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

A Form in a Form$authorForm = new AuthorForm();

$bookForm->embedForm($authorForm);

$choiceForm = new ChoiceForm();

$pollForm->embedFormForEach($choiceForm, 4);

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfForm$form = new AuthorForm();

$input = array('first_name' => 'Fabien', 'last_name' => 'Potencier');

$form->bind($input);if ($form->isValid()){ // do something with the cleaned values $form->getValues();}else{ // do something with the errors $form->getErrorSchema();}

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfForm• sfForm can take default values

$form = new AuthorForm(array('first_name' => 'Fabien'));

• sfFormField objects are widgets bound to theinput or default values

echo $form->getFormField('first_name')->render();

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfForm

$form = new AuthorForm();

echo $form->getFormField('first_name')->render();// ===echo $form['first_name']->render();// ===echo $form['first_name'];

// name and value are bound to the sfFormField object<input type="text" name="first_name" value="Fabien" id="first_name" />

// add some HTML attributesecho $form['first_name']->render(array('class' => 'foo'));

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

For the laziest

echo $form

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Customize your Forms<table>

<tr> <th>

<label for="book_author_first_name">First Name</label>

</th> <td> <ul class="error_list"> <li>Required.</li> </ul> <input type="text" name="book[author][first_name]" id="book_author_first_name" />

</td> </tr> ...</table>

$bookForm['author']

$bookForm['author']['first_name']->renderLabel()

$bookForm['author']['first_name']->renderLabelName()

$bookForm['author']['first_name']->renderError()

$bookForm['author']['first_name']

$bookForm['author']['first_name']->renderRow()

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Forms: In a symfony Actionclass bookActions extends sfActions{ public function executeEdit($request) { $this->form = new AuthorForm();

if ($request->isMethod('post')) { $this->form->bind($request->getParameter('book')); if ($this->form->isValid()) { $values = $this->form->getValues();

$this->redirect('@homepage'); } } }}

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfFormPropel• Generated forms for Propel objects• Fully customizable• Introspects the Propel schema

– Maps Propel/Creole types to symfony validators andwidgets

– Foreign keys– Many to many relationships– Internationalized tables

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

sfFormPropelclass bookActions extends sfActions{ public function executeEdit($request) { $this->book = BookPeer::retrieveByPk($request->getParameter('id')); $this->form = new AuthorForm($this->book);

if ($request->isMethod('post')) { $this->form->bind($request->getParameter('book'); if ($this->form->isValid()) { $book = $this->form->save();

$this->redirect('@book?id='.$book->getId()); } } }}

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Customize Propel Formsclass BookForm extends BaseBookForm{ public function configure() { $this->embedI18n(array('en', 'fr'));

$this->widgetSchema['en']->setLabel('en', 'English');

unset($this['created_at']);

$this->validatorSchema['foo'] = new sfValidatorPass(); $this->widgetSchema['foo'] = new sfWidgetIdentity();

$this->setDefault('published_on', time()); }}

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

A Professional Web Framework

• Built from experience• 1.0 stable, maintained with commercial support• Growing community

– Developers in more than 80 countries– 200 000 visitors per month on symfony-project.com– 200 plugins in just 8 months

• Open-Source Documentation– The book (450 pages - GFDL)– Askeet Tutorial (250 pages)

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Yahoo! uses symfony• Yahoo! Bookmarks

– 20 millions users– Web 2.0 / AJAX

• del.icio.us– New beta on symfony– preview.delicious.com

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Next symfony Workshops

En français : Paris, France - Dec 05, 2007

In English : Paris, France - Feb 13, 2008

More info on www.sensiolabs.com

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

Join Us• Sensio Labs is recruiting in France

– project managers– web developers

• You have a passion for the web?– Web Developer : You have a minimum of 3 years experience in

web development with Open-Source projects and you wish toparticipate to development of Web 2.0 sites using the bestframeworks available.

– Project Manager : You have more than 5 years experience as adeveloper and/or a project manager and you want to managecomplex Web projects for prestigious clients.

International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com

SENSIO S.A.26, rue Salomon de Rothschild

92 286 SURESNES cedexFRANCE

Tél. : +33 1 40 99 80 80Fax : +33 1 40 99 83 34

ContactFabien Potencier

fabien.potencier@sensio.com

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

Recommended