User Interaction and Email Automation in Symfony 1.3: Part1

Exclusive offer: get 50% off this eBook here
Symfony 1.3 Web Application Development

Symfony 1.3 Web Application Development — Save 50%

Design, develop, and deploy feature-rich, high-performance PHP web applications using the Symfony framework

$23.99    $12.00
by Tim Bowler | September 2009 | MySQL Open Source PHP Web Development

After developing an application in Symfony 1.3, the next aspect we will cover is creation of forms. Symfony incorporates a subframework that handles forms, which once mastered, makes creating forms an enjoyable task. In this two-part article by Tim Bowler, we are going to see how easy it is to create and validate forms by creating a newsletter signup module for our web site. We will then convert our new module into a plugin so that we can use it with other projects.

By the end of this article you will know how to:

  • Add a third-party library to send automated emails
  • Create and modify Propel-based forms
  • Use flash variables
  • Create a plugin and package it up for redistribution

The signup module

We want to provide the users with the functionality to enter their name, email address, and how they found our web site. We want all this stored in a database and to have an email automatically sent out to the users thanking them for signing up.

To start things off, we must first add some new tables to our existing database schema.

The structure of our newsletter table will be straightforward. We will need one table to capture the users' information and a related table that will hold the names of all the places where we advertised our site. I have constructed the following entity relationship diagram to show you a visual relationship of the tables:

User Interaction and Email Automation in Symfony 1.3: Part1

All the code used in this article can be accessed here.

Let's translate this diagram into XML and place it in the config/schema.xml file:

<table name="newsletter_adverts" idMethod="native" phpName="NewsletterAds">
<column name="newsletter_adverts_id" type="INTEGER"
required="true" autoIncrement="true"
primaryKey="true" />
<column name="advertised" type="VARCHAR" size="30"
required="true" />
</table>
<table name="newsletter_signups" idMethod="native"
phpName="NewsletterSignup">
<column name="id" type="INTEGER" required="true"
autoIncrement="true" primaryKey="true" />
<column name="first_name" type="VARCHAR" size="20"
required="true" />
<column name="surname" type="VARCHAR" size="20"
required="true" />
<column name="email" type="VARCHAR" size="100"
required="true" />
<column name="activation_key" type="VARCHAR" size="100"
required="true" />
<column name="activated" type="BOOLEAN" default="0"
required="true" />
<column name="newsletter_adverts_id" type="INTEGER"
required="true"/>
<foreign-key foreignTable="newsletter_adverts"
onDelete="CASCADE">
<reference local="newsletter_adverts_id"
foreign="newsletter_adverts_id" />
</foreign-key>
<column name="created_at" type="TIMESTAMP" required="true" />
<column name="updated_at" type="TIMESTAMP" required="true" />
</table>

We will need to populate the newsletter_adverts table with some test data as well. Therefore, I have also appended the following data to the fixtures.yml file located in the data/fixtures/ directory:

NewsletterAds:
nsa1:
advertised: Internet Search
nsa2:
advertised: High Street
nsa3:
advertised: Poster

With the database schema and the test data ready to be inserted into the database, we can once again use the Symfony tasks. As we have added two new tables to the schema, we will have to rebuild everything to generate the models using the following command:

$/home/timmy/workspace/milkshake>symfony propel:build-all-load --no-confirmation

Now we have populated the tables in the database, and the models and forms have been generated for use too.

Binding a form to a database table

Symfony contains a whole framework just for the development of forms. The forms framework makes building forms easier by applying object-oriented methods to their development. Each form class is based on its related table in the database. This includes the fields, the validators, and the way in which the forms and fields are rendered.

A look at the generated base class

Rather than starting off with a simple form, we are going to look at the base form class that has already been generated for us as a part of the build task we executed earlier. Because the code is generated, it will be easier for you to see the initial flow of a form. So let's open the base class for the NewsletterSignupForm form. The file is located at lib/form/base/BaseNewsletterSignupForm.class.php:

class BaseNewsletterSignupForm extends BaseFormPropel
{
public function setup()
{
$this->setWidgets(array(
'id' => new sfWidgetFormInputHidden(),
'first_name' => new sfWidgetFormInput(),
'surname' => new sfWidgetFormInput(),
'email' => new sfWidgetFormInput(),
'activation_key' => new sfWidgetFormInput(),
'activated' => new sfWidgetFormInputCheckbox(),
'newsletter_adverts_id' => new sfWidgetFormPropelChoice
(array('model' => 'NewsletterAds', 'add_empty' => false)),
'created_at' => new sfWidgetFormDateTime(),
'updated_at' => new sfWidgetFormDateTime(),
));
$this->setValidators(array(
'id' => new sfValidatorPropelChoice(array
('model' => 'NewsletterSignup', 'column' => 'id',
'required' => false)),
'first_name' => new sfValidatorString(array('max_length' => 20)),
'surname' => new sfValidatorString(array('max_length' => 20)),
'email' => new sfValidatorString(array('max_length' => 100)),
'activation_key' => new sfValidatorString(array('max_length' => 100)),
'activated' => new sfValidatorBoolean(),
'newsletter_adverts_id'=> new sfValidatorPropelChoice(array
('model' => 'NewsletterAds',
'column' => 'newsletter_adverts_id')),
'created_at' => new sfValidatorDateTime(),
'updated_at' => new sfValidatorDateTime(),
));
$this->widgetSchema->setNameFormat('newsletter_signup[%s]');
$this->errorSchema = new sfValidatorErrorSchema
($this->validatorSchema);
parent::setup();
}

There are five areas in this base class that are worth noting:

  • This base class extends the BaseFormPropel class, which is an empty class. All base classes extend this class, which allows us to add global settings to all our forms.
  • All of the columns in our table are treated as fields in the form, and are referred to as widgets. All of these widgets are then attached to the form by adding them to the setWidgets() method. Looking over the widgets in the array, you will see that they are pretty standard, such as sfWidgetFormInputHidden(), sfWidgetFormInput().
  • However, there is one widget added that follows the relationship between the newsletter_sigups table and the newsletter_adverts table. It is the sfWidgetFormPropelChoice widget. Because there is a 1:M relation between the tables, the default behavior is to use this widget, which creates an HTML drop-down box and is populated with the values from the newsletter_adverts table. As a part of the attribute set, you will see that it has set the model needed to retrieve the values to NewsletterAds and the newsletter_adverts_id column for the actual values of the drop-down box.
  • All the widgets on the form must be validated by default. To do this, we have to call the setValidators() method and add the validation requirements to each widget. At the moment, the generated validators reflect the attributes of our database as set in the schema. For example, the first_name field in the statement 'first_name' => new sfValidatorString(array('max_length' => 20)) demonstrates that the validator checks if the maximum length is 20. If you remember, in our schema too, the first_name column is set to 20 characters.
  • The final part calls the parent's setup() function.

The base class BaseNewsletterSignupForm contains all the components needed to generate the form for us. So let's get the form on a page and take a look at the method to customize it.

There are many widgets that Symfony provides for us. You can find the classes for them inside the widget/ directory of your Symfony installation. The Symfony propel task always generates a form class and its corresponding base class. Of course, not all of our tables will need to have a form bound to them. Therefore, delete all the form classes that are not needed.

Rendering the form

Rendering this basic form requires us to instantiate the form object in the action. Assigning the form object to the global $this variable means that we can pass the form object to the template just like any other variable. So let's start by implementing the newsletter signup module. In your terminal window, execute the generate:module task like this:

$/home/timmy/workspace/milkshake>symfony generate:module frontend signup

Now we can start with the application logic. Open the action class from apps/frontend/modules/signup/actions/actions.class.php for the signup module and add the following logic inside the index action:

public function executeIndex(sfWebRequest $request)
{
$this->form = new NewsletterSignupForm();
return sfView::SUCCESS;
}

As I had mentioned earlier, the form class deals with the form validation and rendering. For the time being, we are going to stick to the default layout by allowing the form object to render itself. Using this method initially will allow us to create rapid prototypes. Let's open the apps/frontend/signup/templates/indexSuccess.php template and add the following view logic:

<form action="<?php echo url_for('signup/submit') ?>" method="POST">
<table><?php echo $form ?></table>
<input type="submit" />
</form>

The form class is responsible for rendering of the form elements only. Therefore, we have to include the <form> and submit HTML tags that wrap around the form. Also, the default format of the form is set to 'table'. Again, we must also add the start and end tags of the <table>.

At this stage, we would normally be able to view the form in the browser. But doing so will raise a Symfony exception error. The cause of this is that the results retrieved from the newsletter_adverts table are in the form of an array of objects. These results need to populate the select box widget. But in the current format, this is not possible. Therefore, we have to convert each object into its string equivalent. To do this, we need to create a PHP magic function of __toString() in the DAO class NewsletterAds.

The DAO class for NewlsetterAds is located at lib/model/NewsletterAds.php just as all of the other models. Here we need to represent each object as its name, which is the value in the advertised column. Remember that we need to add this method to the DAO class as this represents a row within the results, unlike the peer class that represents the entire result set. Let's add the function to the NewsletterAds class as I have done here:

class NewsletterAds extends BaseNewsletterAds
{
public function __toString()
{
return $this->getAdvertised();
}
}

We are now ready to view the completed form. In your web browser, enter the URL http://milkshake/frontend_dev.php/signup and you will see the result shown in the following screenshot:

User Interaction and Email Automation in Symfony 1.3: Part1

As you can see, although the form has been rendered according to our table structure, the fields which we do not want the user to fill in are also included. Of course, we can change this quiet easily. But before we take a look at the layout of the form, let's customize the widgets and widget validators. Now we can begin working on the application logic for submitting the form.

Customizing form widgets and validators

All of the generated form classes are located in the lib/form and the lib/form/base directories. The latter is where the default generated classes are located, and the former is where the customizable classes are located. This follows the same structure as the models.

Each custom form class inherits from its parent. Therefore, we have to override some of the functions to customize the form.

Let's customize the widgets and validators for the NewsletterSignupForm. Open the lib/forms/NewsletterSignupForm.class.php file and paste the following code inside the configure() method:

//Removed unneeded widgets
unset(
$this['created_at'], $this['updated_at'],
$this['activation_key'], $this['activated'], $this['id']
);
//Set widgets
//Modify widgets
$this->widgetSchema['first_name'] = new sfWidgetFormInput();
$this->widgetSchema['newsletter_adverts_id'] = new
sfWidgetFormPropelChoice(array('model' => 'NewsletterAds',
'add_empty' => true, 'label'=>'Where did you find us?'));
$this->widgetSchema['email'] = new sfWidgetFormInput
(array('label' => 'Email Address'));
//Add validation
$this->setValidators(array
('first_name'=> new sfValidatorString(array
('required' => true), array('required' => 'Enter your
firstname')),
'surname'=> new sfValidatorString(array('required' => true),
array('required' => 'Enter your surname')),
'email'=> new sfValidatorString(array('required' => true),
array('invalid' => 'Provide a valid email',
'required' => 'Enter your email')),
'newsletter_adverts_id' => new
sfValidatorPropelChoice(array('model' => 'NewsletterAds',
'column' => 'newsletter_adverts_id'),
array('required' => 'Select where you found us')),
));
//Set post validators
$this->validatorSchema->setPostValidator(
new sfValidatorPropelUnique(array('model' =>
'NewsletterSignup', 'column' => array('email')),
array('invalid' => 'Email address is already registered'))
);
//Set form name
$this->widgetSchema->setNameFormat('newsletter_signup[%s]');
//Set the form format
$this->widgetSchema->setFormFormatterName('list');

Let's take a closer look at the code.

Removing unneeded fields

To remove the fields that we do not want to be rendered, we must call the PHP unset() method and pass in the fields to unset. As mentioned earlier, all of the fields that are rendered need a corresponding validator, unless we unset them. Here we do not want the created_at and activation_key fields to be entered by the user. To do so, the unset() method should contain the following code:

unset(
$this['created_at'], $this['updated_at'],
$this['activation_key'], $this['activated'], $this['id']
);

Modifying the form widgets

Although it'll be fine to use the remaining widgets as they are, let's have a look at how we can modify them:

//Modify widgets
$this->widgetSchema['first_name'] = new sfWidgetFormInput();
$this->widgetSchema['newsletter_adverts_id'] = new
sfWidgetFormPropelChoice(array('model' =>
'AlSignupNewsletterAds', 'add_empty' => true,
'label'=>'Where did you find us?'));
$this->widgetSchema['email'] = new
sfWidgetFormInput(array('label' => 'Email Address'));

There are several types of widgets available, but our form requires only two of them. Here we have used the sfWidgetFormInput() and sfWidgetFormPropelChoice() widgets. Each of these can be initialized with several values. We have initialized the email and newsletter_adverts_id widgets with a label. This basically renders the label field associated to the widget on the form. We do not have to include a label because Symfony adds the label according to the column name.

Adding form validators

Let's add the validators in a similar way as we have added the widgets:

//Add validation
$this->setValidators(array(
'first_name'=> new sfValidatorString(array('required' => true),
array('required' => 'Enter your firstname')),
'surname'=> new sfValidatorString(array('required' => true),
array('required' => 'Enter your surname')),
'email'=> new sfValidatorEmail(array('required' => true),
array('invalid' => 'Provide a valid email',
'required' => 'Enter your email')),
'newsletter_adverts_id' => new sfValidatorPropelChoice(array
('model' => 'NewsletterAds',
'column' => 'newsletter_adverts_id'),
array('required' => 'Select where you found us')),
));
//Set post validators
$this->validatorSchema->setPostValidator(new
sfValidatorPropelUnique(array('model' => 'NewsletterSignup',
'column' => array('email')),
array('invalid' => 'Email address is already registered'))
);

Our form will need four different types of validators:

  • sfValidatorString: This checks the validity of a string against a criteria. It takes four arguments—required, trim, min_length, and max_length.
  • SfValidatorEmail: This validates the input against the pattern of an email address.
  • SfValidatorPropelChoice: It validates the value with the values in the newsletter_adverts table. It needs the model and column that are to be used.
  •    

  • SfValidatorPropelUnique: Again, this validator checks the value against the values in a given table column for uniqueness. In our case, we want to use the NewsletterSignup model to test if the email column is unique.

As mentioned earlier, all the fields must have a validator. Although it's not recommended, you can allow extra parameters to be passed in. To achieve this, there are two steps:

  1. You must disable the default option of having all fields validated by $this->validatorSchema->setOption('allow_extra_fields', true).
  2. Although the above step allows the values to bypass validation, they will be filtered out of the results. To prevent this, you will have to set $this->validatorSchema->setOption('filter_extra_fields', false).

Form naming convention and setting its style

The final part we added is the naming convention for the HTML attributes and the style in which we want the form rendered. The HTML output will use our naming convention. For example, in the following code, we have set the convention to newsletter_signup[fieldname] for each input field's name.

//Set form name
$this->widgetSchema->setNameFormat('newsletter_signup[%s]');
//Set the form format
$this->widgetSchema->setFormFormatterName('list');

Two formats are shipped with Symfony that we can use to render our form. We can either render it in an HTML table or an unordered list. As we have seen, the default is an HTML table. But by setting this as list, the form is now rendered as an unordered HTML list, just like the following screenshot. (Of course, I had to replace the <table> tags with the <ul> tags.)

User Interaction and Email Automation in Symfony 1.3: Part1

Symfony 1.3 Web Application Development Design, develop, and deploy feature-rich, high-performance PHP web applications using the Symfony framework
Published: September 2009
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

Submitting the form

Now that we have the form rendered on our template, we need to handle the application logic for submitting the form. The first step is to add the routing rules for our form page and its submission. The form sits within the signup module, and therefore, we will keep all of the functionality there too. Open the routing file apps/frontend/config/routing.yml, and add the following routes:

signup:
url: /signup
param: { module: signup, action: index}
signup_submit:
url: /signup/submit
param: { module: signup, action: submit}

Here we have created two rules, signup and signup_submit. The first rule will be routed to the index action and the second will be routed to the submit action in our signup module.

Next, we will create the submit action. Open up the actions class from apps/frontend/modules/signup/actions/actions.class.php and create the submit action:

public function executeSubmit(sfWebRequest $request)
{
$this->form = new NewsletterSignupForm();
if ($request->isMethod('post') && $this->form->bindAndSave
($request->getParameter($this->form->getName())))
{
$this->redirect('signup@');
}
//Use the index template as it contains the form
$this->setTemplate('index');
}

Looking over the application logic of our submit action, we first need to instantiate NewsletterSignupForm, after which we will do two things. First, we will test the request method as the form should be a post. This is handled by the $request->isMethod('post') method. Secondly, we need to bind the submitted and cleaned form values to the form that we just instantiated. There are two ways to do this:

  • The first method is to use the bind() function. This will only bind the cleaned values to the form for us. After this, we would need to call the form's save() method to save the form to the database.
  • The second method, which is a little cleaner (and the one I prefer to use), is the form's bindAndSave() method that handles the binding and saving for us.

If the request is a post and the form is valid, the redirect() method is called. (Notice how we can also add the routing label to this method.) We use this method because it forces a redirect, which means if the user clicks on the refresh button, the form will not be re-submitted.

If you wanted to display a thank you page, you can, of course, create another action and a template to handle this. But we will address this later in the article.

You should now be able to go ahead and test the form. In the following screenshot, I have tried to submit the form without a Surname and an incomplete Email Address, which resulted in the anticipated errors:

User Interaction and Email Automation in Symfony 1.3: Part1

Changing the global rendering of forms

So far, we have seen how forms are rendered globally, either as a table or an unordered list. Of course, the forms framework makes the global rendering of forms very easy to change and extend. To demonstrate this, we are going to create our own format. This new format will render the fields within the <div> tags.

To create our own form format, we need to create a new class that extends the sfWidgetFormSchemaFormatter class. The naming convention for the actual class that we will create needs to follow sfWidgetFormSchemaFormatterName. Therefore, our class will be called sfWidgetFormSchemaFormatterDiv.class.php. Because this class will need to be accessed globally, it must be placed in either the lib/ folder or in a subfolder within the lib/ directory. Create a new folder in the lib/ directory called widget, so that the path reflects lib/widget. In this folder, we are going to create our formatter class. Create a file named WidgetFormSchemaFormatterDiv.class.php in the newly created widget/ directory. Open the new file and create the following class:

<?php
class sfWidgetFormSchemaFormatterDiv extends sfWidgetFormSchemaFormatter
{
protected
$rowFormat = '<div style="padding-bottom: 10px;
height: 20px;"><div style="width: 150px;
float: left;">%label% </div>
<div style="width: 300px; float: left">
%field% %error% <br />%help%</div>
<div style="clear: both"></div></div>',
$helpFormat = '<span class="help">%help%</span>',
$errorRowFormat = '<div>%errors%</div>',
$errorListFormatInARow = '%errors%',
$errorRowFormatInARow = '<span class="errorMessage">*%error%
</span>',
$namedErrorRowFormatInARow = '%name%: %error%',
$decoratorFormat = '<div id="formContainer">%content%</div>';
}
?>

Each variable holds a part of the form. I have added the row formatting to the $rowFormat variable. The other variables hold the error messages for the row and the container.

With our new formatter now available, we just have to update the NewsletterSignupForm.class.php file so that it is able to use it. Change the following line

$this->widgetSchema->setFormFormatterName('list');

to

$this->widgetSchema->setFormFormatterName('div');

The final part is to style the error message. In the formatter, I have used a reference to a CSS class called errorMessage. Therefore, in the web/css/main.css file in our stylesheet, I have added the following code:

.errorMessage{ color: red; font-weight: bold}

After refreshing your browser on the signup page (http://milkshake/frontend_dev.php/signup), you will see the new layout along with the validation errors as shown in the following screenshot:

User Interaction and Email Automation in Symfony 1.3: Part1

Customizing the rendering of the form

Till now the rendering of forms has all been handled by the default rendering method. From a prototype point of view, this method has allowed us to create a nice, clean form. The only problem we now face is that there are situations where this rendering is not flexible enough.

To render the form, we have been echoing the $form object in the template. This, in fact, is a shortcut to $form->render(). However, there are several rendering functions available for use. We will be using these to customize our form on the template further. To demonstrate this, I will apply these functions to the email field before presenting to you the final version.

The email section of the form can be broken up into three areas: the label, the form field, and the error message. We are going to use three of the rendering functions to completely customize the email section:

  • Setting a label: For setting the label, we can use the $form['email']->renderLabel() function. You can also customize the label through this function by changing the label and adding the other HTML attributes; for example, $form['email']->renderLabel('Your first name') or $form['email']->renderLabel(null, array('class'=>'myClass')). Of course, this can be set in the form class itself. If it is not set then Symfony will render its column name. The column in our database for this field is called email, which results in 'Email' being returned. For example, if you had a column named email_address, letting Symfony render it would result in 'Email Address' being returned.
  • Displaying the field: Each field can be called by using $form['email']->render() or $form['email'] for short. Once again, the attributes can be passed in as parameters.
  • Handling the error messages: To test if the field does contain an error, we can use the $form['email']->hasError() method. To display the actual error message, we must then call the $form['email']->getError() method.

With this in mind for the moment, I am now going to show you how we will render the email row on our form:

<div style="height: 30px;">
<div style="width: 150px; float: left"><?php echo $form['email']->
renderLabel() ?></div>
<?php echo $form['email']->render(($form['email']->hasError())?
array('class'=>'boxError'): array('class'=>'box')) ?>
<?php echo ($form['email']->hasError())?
'<span class="errorMessage">* '.$form['email']->getError().
'</span>': '' ?>
</div>
<div style="clear: both"></div>
</div>

As you can see, we now have a complete control over how a form is rendered. To complete rendering, I have applied this technique to the other form fields too with the following code:

<form action="<?php echo url_for('@signup_submit') ?>" method="post"
name="Newsletter">
<div style="height: 30px;">
<div style="width: 150px; float: left">
<?php echo $form['first_name']->renderLabel() ?></div>
<?php echo $form['first_name']->render(($form['first_name']->
hasError())? array('class'=>'boxError'):
array('class'=>'box')) ?>
<?php echo ($form['first_name']->hasError())?
' <span class="errorMessage">* '.$form['first_name']->
getError(). '</span>': '' ?>
<div style="clear: both"></div>
</div>
<div style="height: 30px;">
<div style="width: 150px; float: left">
<?php echo $form['surname']->renderLabel() ?></div>
<div style="width: 300px; float: left;">
<?php echo $form['surname']->render(($form['surname']->
hasError())? array('class'=>'boxError'):
array('class'=>'box')) ?>
<?php echo ($form['surname']->hasError())?
' <span class="errorMessage">*
'.$form['surname']->getError(). '</span>': '' ?>
</div>
<div style="clear: both"></div>
</div>
<div style="height: 30px;">
<div style="width: 150px; float: left">
<?php echo $form['email']->renderLabel() ?></div>
<?php echo $form['email']->render(($form['email']->
hasError())? array('class'=>'boxError'):
array('class'=>'box')) ?>
<?php echo ($form['email']->hasError())?
' <span class="errorMessage">* '.$form['email']->
getError(). '</span>': '' ?>
</div>
<div style="clear: both"></div>
</div>
<div style="height: 30px;">
<div style="width: 150px; float: left">
<?php echo $form['newsletter_adverts_id']->renderLabel() ?>
</div>
<?php echo $form['newsletter_adverts_id']->
render(($form['newsletter_adverts_id']->hasError())?
array('class'=>'boxError'): array('class'=>'box')) ?>
<?php echo ($form['newsletter_adverts_id']->hasError())?
' <span class="errorMessage">*
'.$form['newsletter_adverts_id']->getError().
'</span>': '' ?>
<div style="clear: both"></div>
</div>
<?php echo $form['_csrf_token']; ?>
<input type="submit" />
</form>

You must have noted that I have added a reference to another CSS class, boxError. The following code too has been added to the main.css stylesheet located in the web/css/ directory:

.boxError{ border: 2px solid red;}

Form security for the user

When we created our project, there were many default settings configured for us. One such setting concerns the forms and Cross Site Request Forgery (CSRF). To help prevent this, all forms must contain a hidden field called csr_token. As you can see in our previous form, I have included this hidden field just above the submit button. This value is derived from csrf_secret, located in the application's settings file at apps/frontend/config/settings.yml. Although it is randomly generated, it is advised that you should change this.

After you have completed the template by adding the above code and the CSS, check out the form at http://milkshake/frontend_dev.php/signup in your browser. In the following screenshot, I have again tried to submit the form containing errors:

User Interaction and Email Automation in Symfony 1.3: Part1

As you can see, the error messages appear with a red border around the input fields.

To finish off the signup section, we will need to create a link to the signuppage. I have placed my navigation link on the lefthand side of the page alongwith all the other links. To do this, open up the application layout template from apps/frontend/templates/layout.php and add the following underneath the other navigation links:

<li style="margin:0;padding:0.25em 0.5em 0.25em 0.5em; width: 150px;
border-bottom: 1px solid #000000; border-right: 1px
solid #000000; ">
<?php echo link_to('Newsletter', '@signup') ?></li>

Notice how we can still use the routing tag name @signup to reference the route.

Creating a simple form

We have seen how powerful forms in Symfony are, especially when bounded to a database table. Not all forms, though, will need to reference a database table. Instead, we can create a simple form for which the process is practically the same. The only difference is that we have to manually create the form class.

Just to give you an example, let's say we want to create a simple feedback form that contains three fields called name, email, and message. You would create the new form class at the same place where all of the other form classes are stored—in lib/form/—and perhaps, name the file as FeedbackForm.class.php. With this created, let's create the class as follows:

class FeedbackForm extends BaseForm
{
public function configure()
{
$this->setWidgets(array(
'name' => new sfWidgetFormInput(),
'email' => new sfWidgetFormInput(),
'message' => new sfWidgetFormTextarea(),
));
//Add validation
$this->setValidators(array(
'name'=> new sfValidatorString(array('required' => true),
array('required' => 'Enter your firstname')),
'email'=> new sfValidatorEmail(array
('required' => true),
array('invalid' => 'Provide a valid email',
'required' => 'Enter your email')),
'message'=> new sfValidatorString(array('required' => true),
array('required' => 'Enter your surname')),
));
$this->widgetSchema->setNameFormat('feedback[%s]');
//Set the form format
$this->widgetSchema->setFormFormatterName('div');
}
}

Comparing this to our Propel, the only difference is that the parent class is BaseForm, as the same logic applies to the forms. If you look at the Propel forms class hierarchy too, both eventually extend sfForm, as sfForm is the parent class for all forms.

The form submission process is also handled in exactly the same way as Propel. However, you only need to call the bind() method, as there is no database table to save the form object. The following code shows the form submission process:

$this->form = new FeedbackForm();
if($request->isMethod('post') && $this->form->bind
($request->getParameter($this->form->getName())))
{
//Code
}

There are two possible ways to retrieve the values from a simple form:

  • Using the $request object, you can retrieve all of the fields. For example, suppose you wanted to retrieve the email field, you would call $request->getParameter('feedback[email]'). If you wanted to access all of the fields as an array, you would use $request->getParameter('feedback').
  • After calling the bind() method, all of the form values are then bound to the form object. To retrieve the email field, for example, you would use $this->form->getValue('email'). For accessing all the values as an array, you would call $this->form->getValues().

Although I have presented you with two ways to retrieve the form values, it is extremely important that you access the form values through the form object as shown in the latter of the two ways. This is because the values there have been cleaned.

>> Continue Reading: User Interaction and Email Automation in Symfony 1.3: Part2

 

If you have read this article you may be interested to view :

 

Symfony 1.3 Web Application Development Design, develop, and deploy feature-rich, high-performance PHP web applications using the Symfony framework
Published: September 2009
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

About the Author :


Tim Bowler

Tim Bowler has a Bachelor's Degree in Computer Science, a Masters Degree in Internet Technologies and E-Commerce, and is currently studying for his PhD part time. He has over 10 years of experience in web application development and project management. His experience and determination has gained him membership in the Institute of Engineering and Technology and he is a Chartered IT Professional. Tim started his career developing web applications in PHP4 for a digital media agency in London. Later he introduced agile and scrum into the development process along with Symfony. Tim is now the Managing Director of Agile Labs which is a web application development and agile coaching company.

Books From Packt

jQuery 1.3 with PHP
jQuery 1.3 with PHP

Ext JS 3.0 Cookbook
Ext JS 3.0 Cookbook

PHP Team Development
PHP Team Development

Matplotlib for Python Developers
Matplotlib for Python Developers

Apache MyFaces Trinidad 1.2: A Practical Guide
Apache MyFaces Trinidad 1.2: A Practical Guide

3D Game Development with Microsoft Silverlight 3: Beginner's Guide
3D Game Development with Microsoft Silverlight 3: Beginner's Guide

Zend Framework 1.8 Web Application Development
Zend Framework 1.8 Web Application Development

Joomla! 1.5x Customization: Make Your Site Adapt to Your Needs
Joomla! 1.5x Customization: Make Your Site Adapt to Your Needs

Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software