Link: http://www.symfony-project.org/forms/1_2/en/
sfForm object
a form is an object inheriting from the sfForm class
Widgets
Every form field is a
widget.
// lib/form/ContactForm.class.php
class ContactForm
extends sfForm
{
public function configure
()
{
$this->setWidgets(array(
'name' => new sfWidgetFormInput
(),
'email' => new sfWidgetFormInput
(),
'message' => new sfWidgetFormTextarea
(),
));
}
} setWidgets gets an associative array:
- Keys field name
- Values widget object
Displaying the form
// apps/frontend/modules/contact/actions/actions.class.php
class contactActions extends sfActions
{
public function executeIndex()
{
$this->form = new ContactForm();
}
}
// apps/frontend/modules/contact/templates/indexSuccess.php
<form action="
<?php echo url_for
('contact/submit') ?>" method="POST">
<table>
<?php echo $form ?>
<tr>
<td colspan="2">
<input type="submit" />
</td>
</tr>
</table>
</form>
Generated HTML
<form action="/frontend_dev.php/contact/submit" method="POST">
<table>
<!-- Beginning of generated code by
<?php echo $form ?>
-->
<tr>
<th><label for="name">Name</label></th>
<td><input type="text" name="name" id="name" /></td>
</tr>
<tr>
<th><label for="email">Email</label></th>
<td><input type="text" name="email" id="email" /></td>
</tr>
<tr>
<th><label for="message">Message</label></th>
<td><textarea rows="4" cols="30" name="message" id="message"></textarea></td>
</tr>
<!-- End of generated code by
<?php echo $form ?>
-->
<tr>
<td colspan="2">
<input type="submit" />
</td>
</tr>
</table>
</form>
Labels
Are created automatically but you can change them
$this->setWidgets(array(
'first_name' => new sfWidgetFormInput
(), // generated label: "First name"
'last_name' => new sfWidgetFormInput
(), // generated label: "Last name"
));
$this->widgetSchema->setLabels(array(
'name' => 'Your name',
'email' => 'Your email address',
'message' => 'Your message',
));
Modify single label:
$this->widgetSchema->setLabel('email', 'Your email address');
WidgetSchema object
$this->setWidgetSchema(new sfWidgetFormSchema
(array(
'name' => new sfWidgetFormInput
(),
'email' => new sfWidgetFormInput
(),
'message' => new sfWidgetFormTextarea
(),
)));
// almost equivalent to :
$this->widgetSchema = new sfWidgetFormSchema
(array(
'name' => new sfWidgetFormInput
(),
'email' => new sfWidgetFormInput
(),
'message' => new sfWidgetFormTextarea
(),
)); table or list
class ContactForm
extends sfForm
{
public function configure
()
{
$this->setWidgets(array(
'name' => new sfWidgetFormInput
(),
'email' => new sfWidgetFormInput
(),
'message' => new sfWidgetFormTextarea
(),
));
$this->widgetSchema->setFormFormatterName('list');
}
} Submission
public function executeSubmit($request)
{
$this->forward404Unless($request->isMethod('post'));
$params = array(
'name' => $request->getParameter('name'),
'email' => $request->getParameter('email'),
'message' => $request->getParameter('message'),
);
$this->redirect('contact/thankyou?'.http_build_query($params));
}
public function executeThankyou()
{
}
// apps/frontend/modules/contact/templates/thankyouSuccess.php
<ul>
<li>Name:
<?php echo $sf_params->get('name') ?></li>
<li>Email:
<?php echo $sf_params->get('email') ?></li>
<li>Message:
<?php echo $sf_params->get('message') ?></li>
</ul>
Get in an array form
class ContactForm
extends sfForm
{
public function configure
()
{
$this->setWidgets(array(
'name' => new sfWidgetFormInput
(),
'email' => new sfWidgetFormInput
(),
'message' => new sfWidgetFormTextarea
(),
));
$this->widgetSchema->setNameFormat('contact[%s]');
}
}
and use it like this:
public function executeSubmit($request)
{
$this->forward404Unless($request->isMethod('post'));
$this->redirect('contact/thankyou?'.http_build_query($request->getParameter('contact')));
}
Merge display and get methods
class contactActions extends sfActions
{
public function executeIndex($request)
{
$this->form = new ContactForm();
if ($request->isMethod('post'))
{
$this->redirect('contact/thankyou?'.http_build_query($request->getParameter('contact')));
}
}
}
class ContactForm
extends sfForm
{
protected
static $subjects = array('Subject A', 'Subject B', 'Subject C');
public function configure
()
{
$this->setWidgets(array(
'name' => new sfWidgetFormInput
(),
'email' => new sfWidgetFormInput
(),
'subject' => new sfWidgetFormSelect
(array('choices' => self::$subjects)),
'message' => new sfWidgetFormTextarea
(),
));
$this->widgetSchema->setNameFormat('contact[%s]');
}
} See the widget API
At http://www.symfony-project.org/api/1_2/widget
Each widget takes
options and
attributes. The latter is HTML attributes.
$emailWidget = new sfWidgetFormInput
(array(), array('class' => 'email'));
// Generated HTML
<input type
="text" name
="contact[email]" class="email" id
="contact_email" />
$emailWidget = new sfWidgetFormInput
(array(), array('class' => 'email', 'id' => 'email'));
// Generated HTML
<input type
="text" name
="contact[email]" class="email" id
="email" /> Default Values
class ContactForm
extends sfForm
{
public function configure
()
{
// ...
$this->setDefault('email', 'Your Email Here');
$this->setDefaults(array('email' => 'Your Email Here', 'name' => 'Your Name Here'));
}
} Default values can be set at the time of form creation
public function executeIndex
($request)
{
$this->form = new ContactForm
(array('email' => 'Your Email Here', 'name' => 'Your Name Here'));
// ...
} Example
name
: optional
email
: mandatory, the value must be a valid email address
subject
: mandatory, the selected value must be valid to a list of values
message
: mandatory, the length of the message must be at least four characters

Creation
// lib/form/ContactForm.class.php
class ContactForm
extends sfForm
{
protected
static $subjects = array('Subject A', 'Subject B', 'Subject C');
public function configure
()
{
$this->setWidgets(array(
'name' => new sfWidgetFormInput
(),
'email' => new sfWidgetFormInput
(),
'subject' => new sfWidgetFormSelect
(array('choices' => self::$subjects)),
'message' => new sfWidgetFormTextarea
(),
));
$this->widgetSchema->setNameFormat('contact[%s]');
$this->setValidators(array(
'name' => new sfValidatorString
(array('required' => false)),
'email' => new sfValidatorEmail
(),
'subject' => new sfValidatorChoice
(array('choices' => array_keys(self::$subjects))),
'message' => new sfValidatorString
(array('min_length' => 4)),
));
}
}
each validator can take some options like
required or
trim Update your actions
class contactActions extends sfActions
{
public function executeIndex($request)
{
$this->form = new ContactForm();
if ($request->isMethod('post'))
{
$this->form->bind($request->getParameter('contact'));
if ($this->form->isValid())
{
$this->redirect('contact/thankyou?'.http_build_query($this->form->getValues()));
}
}
}
public function executeThankyou()
{
}
}
Bound vs. Initial States
Also,
$this->redirect('contact/thankyou?'.http_build_query($this->form->getValues()));
vs.
$this->redirect('contact/thankyou?'.http_build_query($this->getParameter('contact')));
Validator Customization
class ContactForm
extends sfForm
{
protected
static $subjects = array('Subject A', 'Subject B', 'Subject C');
public function configure
()
{
// ...
$this->setValidators(array(
'name' => new sfValidatorString
(array('required' => false)),
'email' => new sfValidatorEmail
(array(), array('invalid' => 'The email address is invalid.')),
'subject' => new sfValidatorChoice
(array('choices' => array_keys(self::$subjects))),
'message' => new sfValidatorString
(array('min_length' => 4), array('required' => 'The message field is required.')),
));
}
}
Or use dynamic data
class ContactForm
extends sfForm
{
public function configure
()
{
// ...
$this->setValidators(array(
'name' => new sfValidatorString
(array('required' => false)),
'email' => new sfValidatorEmail
(array(), array('invalid' => 'Email address is invalid.')),
'subject' => new sfValidatorChoice
(array('choices' => array_keys(self::$subjects))),
'message' => new sfValidatorString
(array('min_length' => 4), array(
'required' => 'The message field is required',
'min_length' => 'The message "%value%" is too short. It must be of %min_length% characters at least.',
)),
));
}
} Equivalent
$this->setValidatorSchema(new sfValidatorSchema
(array(
'email' => new sfValidatorEmail
(),
'subject' => new sfValidatorChoice
(array('choices' => array_keys(self::$subjects))),
'message' => new sfValidatorString
(array('min_length' => 4)),
)));
Extra Field?
Good to prevent injection!
Disable it:
$this->validatorSchema->setOption('allow_extra_fields', true);
But this filters extra fields. You can, however, disable this:
$this->validatorSchema->setOption('filter_extra_fields', false);
Logical Validators
class ContactForm
extends sfForm
{
public function configure
()
{
// ...
$this->setValidators(array(
// ...
'name' => new sfValidatorAnd
(array(
new sfValidatorString
(array('min_length' => 5)),
new sfValidatorRegex
(array('pattern' => '/[\w- ]+/')),
)),
));
}
} Global Validators
$this->validatorSchema->setPostValidator(
new sfValidatorSchemaCompare('password', sfValidatorSchemaCompare::EQUAL, 'password_again'));
OR
$this->validatorSchema->setPostValidator(new sfValidatorSchemaCompare('password', '==', 'password_again'));
More...
$this->validatorSchema->setPostValidator(new sfValidatorAnd
(array(
new sfValidatorSchemaCompare
('start_date', sfValidatorSchemaCompare
::LESS_THAN_EQUAL, 'end_date'),
new sfValidatorSchemaCompare
('password', sfValidatorSchemaCompare
::EQUAL, 'password_again'),
))); File Upload
// lib/form/ContactForm.class.php
class ContactForm
extends sfForm
{
protected
static $subjects = array('Subject A', 'Subject B', 'Subject C');
public function configure
()
{
$this->setWidgets(array(
'name' => new sfWidgetFormInput
(),
'email' => new sfWidgetFormInput
(),
'subject' => new sfWidgetFormSelect
(array('choices' => self::$subjects)),
'message' => new sfWidgetFormTextarea
(),
'file' => new sfWidgetFormInputFile
(),
));
$this->widgetSchema->setNameFormat('contact[%s]');
$this->setValidators(array(
'name' => new sfValidatorString
(array('required' => false)),
'email' => new sfValidatorEmail
(),
'subject' => new sfValidatorChoice
(array('choices' => array_keys(self::$subjects))),
'message' => new sfValidatorString
(array('min_length' => 4)),
'file' => new sfValidatorFile
(),
));
}
}
<form action="
<?php echo url_for
('contact/index') ?>" method="POST" enctype="multipart/form-data">
<table>
<?php echo $form ?>
<tr>
<td colspan="2">
<input type="submit" />
</td>
</tr>
</table>
</form>
Bind
class contactActions extends sfActions
{
public function executeIndex($request)
{
$this->form = new ContactForm();
if ($request->isMethod('post'))
{
$this->form->bind($request->getParameter('contact'), $request->getFiles('contact'));
if ($this->form->isValid())
{
$values = $this->form->getValues();
// do something with the values
// ...
}
}
}
public function executeThankyou()
{
}
}
File Process
if ($this->form->isValid())
{
$file = $this->form->getValue('file');
$filename = 'uploaded_'.sha1($file->getOriginalName());
$extension = $file->getExtension($file->getOriginalExtension());
$file->save(sfConfig
::get('sf_upload_dir').'/'.$filename.$extension);
// ...
}
Generates
<form action="/frontend_dev.php/contact" method="POST">
<table>
<tr>
<th><label for="contact_name">Name</label></th>
<td><input type="text" name="contact[name]" id="contact_name" /></td>
</tr>
<tr>
<th><label for="contact_email">Email</label></th>
<td>
<ul class="error_list">
<li>This email address is invalid.</li>
</ul>
<input type="text" name="contact[email]" value="fabien" id="contact_email" />
</td>
</tr>
<tr>
<th><label for="contact_subject">Subject</label></th>
<td>
<select name="contact[subject]" id="contact_subject">
<option value="0" selected="selected">Subject A</option>
<option value="1">Subject B</option>
<option value="2">Subject C</option>
</select>
</td>
</tr>
<tr>
<th><label for="contact_message">Message</label></th>
<td>
<ul class="error_list">
<li>The message "foo" is too short. It must be of 4 characters at least.</li>
</ul>
<textarea rows="4" cols="30" name="contact[message]" id="contact_message">foo</textarea>
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" />
</td>
</tr>
</table>
</form>

Each field contains

Global Customizatoin
Instead, use
<?php echo $form->render(array('email' => array('class' => 'email'))) ?>
renderRow
<form action="
<?php echo url_for
('contact/index') ?>" method="POST">
<table>
<?php echo $form['name']->renderRow() ?>
<?php echo $form['email']->renderRow() ?>
<?php echo $form['subject']->renderRow() ?>
<?php echo $form['message']->renderRow() ?>
<tr>
<td colspan="2">
<input type="submit" />
</td>
</tr>
</table>
</form>
Now you can customize it
<form action="
<?php echo url_for
('contact/index') ?>" method="POST">
<table>
<?php echo $form['name']->renderRow() ?>
<?php echo $form['email']->renderRow(array('class' => 'email')) ?>
<?php echo $form['subject']->renderRow() ?>
<?php echo $form['message']->renderRow(array(), 'Your message') ?>
<tr>
<td colspan="2">
<input type="submit" />
</td>
</tr>
</table>
</form>
Further Customization

Generating Forms
./symfony propel:build-forms
Usage
// apps/frontend/modules/author/actions/actions.class.php
class authorActions extends sfActions
{
public function executeIndex()
{
$this->authorList = AuthorPeer::doSelect(new Criteria());
}
public function executeCreate()
{
$this->form = new AuthorForm();
$this->setTemplate('edit');
}
public function executeEdit($request)
{
$this->form = new AuthorForm(AuthorPeer::retrieveByPk($request->getParameter('id')));
}
public function executeUpdate($request)
{
$this->forward404Unless($request->isMethod('post'));
$this->form = new AuthorForm(AuthorPeer::retrieveByPk($request->getParameter('id')));
$this->form->bind($request->getParameter('author'));
if ($this->form->isValid())
{
$author = $this->form->save();
$this->redirect('author/edit?id='.$author->getId());
}
$this->setTemplate('edit');
}
public function executeDelete($request)
{
$this->forward404Unless($author = AuthorPeer::retrieveByPk($request->getParameter('id')));
$author->delete();
$this->redirect('author/index');
}
}
A Propel form instance is always connected to a Propel object
// creating a new object
$authorForm = new AuthorForm
();
print $authorForm->getObject()->getId(); // outputs null
print $authorForm->isNew(); // outputs true
// modifying an existing object
$author = AuthorPeer
::retrieveByPk(1);
$authorForm = new AuthorForm
($author);
print $authorForm->getObject()->getId(); // outputs 1
print $authorForm->isNew(); // outputs false