The definitive guide of Symfony 1.2

10.2. Form Helpers for Objects

When form elements are used to edit the properties of an object, standard link helpers can become tedious to write. For instance, to edit the telephone attribute of a Customer object, you would write this:

<?php echo input_tag('telephone', $customer->getTelephone()) ?>
=> <input type="text" name="telephone" id="telephone" value="0123456789" />

To avoid repeating the attribute name, symfony provides an alternative object form helper for each form helper. An object form helper deduces the name and the default value of a form element from an object and a method name. The previous input_tag() is equivalent to this:

<?php echo object_input_tag($customer, 'getTelephone') ?>
=> <input type="text" name="telephone" id="telephone" value="0123456789" />

The economy might not look crucial for the object_input_tag(). However, every standard form helper has a corresponding object form helper, and they all share the same syntax. It makes generation of forms quite straightforward. That's why the object form helpers are used extensively in the scaffolding and generated administrations (see Chapter 14). Listing 10-11 lists the object form helpers.

Listing 10-11 - Object Form Helpers Syntax

<?php echo object_input_tag($object, $method, $options) ?>
<?php echo object_input_date_tag($object, $method, $options) ?>
<?php echo object_input_hidden_tag($object, $method, $options) ?>
<?php echo object_textarea_tag($object, $method, $options) ?>
<?php echo object_checkbox_tag($object, $method, $options) ?>
<?php echo object_select_tag($object, $method, $options) ?>
<?php echo object_select_country_tag($object, $method, $options) ?>
<?php echo object_select_language_tag($object, $method, $options) ?>

There is no object_password_tag() helper, since it is a bad practice to give a default value to a password tag, based on something the user has previously entered.

Caution Unlike the regular form helpers, the object form helpers are available only if you declare explicitly the use of the Object helper group in your template with use_helper('Object').

The most interesting of all object form helpers are objects_for_select() and object_select_tag(), which concern drop-down lists.

10.2.1. Populating Drop-Down Lists with Objects

The options_for_select() helper, described previously with the other standard helpers, transforms a PHP associative array into an options list, as shown in Listing 10-12.

Listing 10-12 - Creating a List of Options Based on an Array with options_for_select()

<?php echo options_for_select(array(
  '1' => 'Steve',
  '2' => 'Bob',
  '3' => 'Albert',
  '4' => 'Ian',
  '5' => 'Buck'
), 4) ?>
 => <option value="1">Steve</option>
    <option value="2">Bob</option>
    <option value="3">Albert</option>
    <option value="4" selected="selected">Ian</option>
    <option value="5">Buck</option>

Suppose that you already have an array of objects of class Author, resulting from a Propel query. If you want to build a list of options based on this array, you will need to loop on it to retrieve the id and the name of each object, as shown in Listing 10-13.

Listing 10-13 - Creating a List of Options Based on an Array of Objects with options_for_select()

// In the action
$options = array();
foreach ($authors as $author)
{
  $options[$author->getId()] = $author->getName();
}
$this->options = $options;

// In the template
<?php echo options_for_select($options, 4) ?>

This kind of processing happens so often that symfony has a helper to automate it: objects_for_select(), which creates an option list based directly on an array of objects. The helper needs two additional parameters: the method names used to retrieve the value and the text contents of the <option> tags to be generated. So Listing 10-13 is equivalent to this simpler form:

<?php echo objects_for_select($authors, 'getId', 'getName', 4) ?>

That's smart and fast, but symfony goes even further, when you deal with foreign key columns.

10.2.2. Creating a Drop-Down List Based on a Foreign Key Column

The values a foreign key column can take are the primary key values of the foreign table records. If, for instance, the article table has an author_id column that is a foreign key to an author table, the possible values for this column are the id of all the records of the author table. Basically, a drop-down list to edit the author of an article would look like Listing 10-14.

Listing 10-14 - Creating a List of Options Based on a Foreign Key with objects_for_select()

<?php echo select_tag('author_id', objects_for_select(
  AuthorPeer::doSelect(new Criteria()),
  'getId',
  '__toString',
  $article->getAuthorId()
)) ?>
=> <select name="author_id" id="author_id">
      <option value="1">Steve</option>
      <option value="2">Bob</option>
      <option value="3">Albert</option>
      <option value="4" selected="selected">Ian</option>
      <option value="5">Buck</option>
    </select>

The object_select_tag() does all that by itself. It displays a drop-down list populated with the name of the possible records of the foreign table. The helper can guess the foreign table and foreign column from the schema, so its syntax is very concise. Listing 10-13 is equivalent to this:

<?php echo object_select_tag($article, 'getAuthorId') ?>

The object_select_tag() helper guesses the related peer class name (AuthorPeer in the example) based on the method name passed as a parameter. However, you can specify your own class by setting the related_class option in the third argument. The text content of the <option> tags is the record name, which is the result of the __toString() method of the object class (if $author->__toString() method is undefined, the primary key is used instead). In addition, the list of options is built from a doSelect() method with an empty criteria value; it returns all the records ordered by creation date. If you prefer to display only a subset of records with a specific ordering, create a method in the peer class returning this selection as an array of objects, and set it in the peer_method option. Lastly, you can add a blank option or a custom option at the top of the drop-down list by setting the include_blank and include_custom options. Listing 10-15 demonstrates these different options for the object_select_tag() helper.

Listing 10-15 - Options of the object_select_tag() Helper

// Base syntax
<?php echo object_select_tag($article, 'getAuthorId') ?>
// Builds the list from AuthorPeer::doSelect(new Criteria())

// Change the peer class used to retrieve the possible values
<?php echo object_select_tag($article, 'getAuthorId', 'related_class=Foobar') ?>
// Builds the list from FoobarPeer::doSelect(new Criteria())

// Change the peer method used to retrieve the possible values
<?php echo object_select_tag($article, 'getAuthorId','peer_method=getMostFamousAuthors') ?>
// Builds the list from AuthorPeer::getMostFamousAuthors(new Criteria())

// Add an <option value="">&nbsp;</option> at the top of the list
<?php echo object_select_tag($article, 'getAuthorId', 'include_blank=true') ?>

// Add an <option value="">Choose an author</option> at the top of the list
<?php echo object_select_tag($article, 'getAuthorId',
  'include_custom=Choose an author') ?>

10.2.3. Updating Objects

A form completely dedicated to editing object properties by using object helpers is easier to handle in an action. For instance, if you have an object of class Author with name, age, and address attributes, the form can be coded as shown in Listing 10-16.

Listing 10-16 - A Form with Only Object Helpers

<?php echo form_tag('author/update') ?>
  <?php echo object_input_hidden_tag($author, 'getId') ?>
  Name: <?php echo object_input_tag($author, 'getName') ?><br />
  Age:  <?php echo object_input_tag($author, 'getAge') ?><br />
  Address: <br />
         <?php echo object_textarea_tag($author, 'getAddress') ?>
</form>

The update action of the author module, called when the form is submitted, can simply update the object with the fromArray() modifier generated by Propel, as shown in Listing 10-17.

Listing 10-17 - Handling a Form Submission Based on Object Form Helpers

public function executeUpdate($request)
{
  $author = AuthorPeer::retrieveByPk($request->getParameter('id'));
  $this->forward404Unless($author);

  $author->fromArray($this->getRequest()->getParameterHolder()->getAll(),BasePeer::TYPE_FIELDNAME);
  $author->save();

  return $this->redirect('/author/show?id='.$author->getId());
}