The definitive guide of Symfony 1.1

14.3. Generator Configuration

The generator configuration file is very powerful, allowing you to alter the generated administration in many ways. But such capabilities come with a price: The overall syntax description is long to read and learn, making this chapter one of the longest in this book. The symfony website proposes an additional resource that will help you learn this syntax: the administration generator cheat sheet, reproduced in Figure 14-7. Download it from http://www.symfony-project.org/uploads/assets/sfAdminGeneratorRefCard.pdf, and keep it close to you when you read the following examples of this chapter.

The examples of this section will tweak the article administration module, as well as the comment administration module, based on the Comment class definition. Create the latter with the propel:init-admin task:

> php symfony propel:init-admin backend comment Comment
The administration generator cheat sheet

Figure 14.4 The administration generator cheat sheet

14.3.1. Fields

By default, the columns of the list view and the fields of the edit view are the columns defined in schema.yml. With generator.yml, you can choose which fields are displayed, which ones are hidden, and add fields of your own — even if they don't have a direct correspondence in the object model.

14.3.1.1. Field Settings ###

The administration generator creates a field for each column in the schema.yml file. Under the fields key, you can modify the way each field is displayed, formatted, etc. For instance, the field settings shown in Listing 14-7 define a custom label class and input type for the title field, and a label and a tooltip for the content field. The following sections will describe in detail how each parameter works.

Listing 14-7 - Setting a Custom Label for a Column

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default

    fields:
      title:          { name: Article Title, type: textarea_tag, params: class=foo }
      content:        { name: Body, help: Fill in the article body }

In addition to this default definition for all the views, you can override the field settings for a given view (list and edit), as demonstrated in Listing 14-8.

Listing 14-8 - Overriding Global Settings View per View

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default

    fields:
      title:          { name: Article Title }
      content:        { name: Body }

    list:
      fields:
        title:        { name: Title }

    edit:
      fields:
        content:      { name: Body of the article }

This is a general principle: Any settings that are set for the whole module under the fields key can be overridden by view-specific (list and edit) areas that follow.

14.3.1.2. Adding Fields to the Display ###

The fields that you define in the fields section can be displayed, hidden, ordered, and grouped in various ways for each view. The display key is used for that purpose. For instance, to arrange the fields of the comment module, use the code of Listing 14-9.

Listing 14-9 - Choosing the Fields to Display, in modules/comment/config/generator.yml

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Comment
    theme:            default

    fields:
      article_id:     { name: Article }
      created_at:     { name: Published on }
      content:        { name: Body }

    list:
      display:        [id, article_id, content]

    edit:
      display:
        NONE:         [article_id]
        Editable:     [author, content, created_at]

The list will then display three columns, as in Figure 14-8, and the edit form will display four fields, assembled in two groups, as in Figure 14-9.

Custom column setting in the list view of the comment module

Figure 14.5 Custom column setting in the list view of the comment module

Grouping fields in the edit view of the comment module

Figure 14.6 Grouping fields in the edit view of the comment module

So you can use the display setting in two ways:

  • To select the columns to display and the order in which they appear, put the fields in a simple array — as in the previous list view.
  • To group fields, use an associative array with the group name as a key, or NONE for a group with no name. The value is still an array of ordered column names.

Tip By default, the primary key columns never appear in either view.

14.3.1.3. Custom Fields ###

As a matter of fact, the fields configured in generator.yml don't even need to correspond to actual columns defined in the schema. If the related class offers a custom getter, it can be used as a field for the list view; if there is a getter and/or a setter, it can also be used in the edit view. For instance, you can extend the Article model with a getNbComments() method similar to the one in Listing 14-10.

Listing 14-10 - Adding a Custom Getter in the Model, in lib/model/Article.php

public function getNbComments()
{
  return $this->countComments();
}

Then nb_comments is available as a field in the generated module (notice that the getter uses a camelCase version of the field name), as in Listing 14-11.

Listing 14-11 - Custom Getters Provide Additional Columns for Administration Modules, in backend/modules/article/config/generator.yml

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default

    list:
      display:        [id, title, nb_comments, created_at]

The resulting list view of the article module is shown in Figure 14-10.

Custom field in the list view of the article module

Figure 14.7 Custom field in the list view of the article module

Custom fields can even return HTML code to display more than raw data. For instance, you can extend the Comment class with a getArticleLink() method as in Listing 14-12.

Listing 14-12 - Adding a Custom Getter Returning HTML, in lib/model/Comment.class.php

public function getArticleLink()
{
  return link_to($this->getArticle()->getTitle(), 'article/edit?id='.$this->getArticleId());
}

You can use this new getter as a custom field in the comment/list view with the same syntax as in Listing 14-11. See the example in Listing 14-13, and the result in Figure 14-11, where the HTML code output by the getter (a hyperlink to the article) appears in the second column instead of the article primary key.

Listing 14-13 - Custom Getters Returning HTML Can Also Be Used As Additional Columns, in modules/comment/config/generator.yml

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Comment
    theme:            default

    list:
      display:        [id, article_link, content]
Custom field in the list view of the comment module

Figure 14.8 Custom field in the list view of the comment module

14.3.1.4. Partial Fields ###

The code located in the model must be independent from the presentation. The example of the getArticleLink() method earlier doesn't respect this principle of layer separation, because some view code appears in the model layer. To achieve the same goal in a correct way, you'd better put the code that outputs HTML for a custom field in a partial. Fortunately, the administration generator allows it if you declare a field name prefixed by an underscore. In that case, the generator.yml file of Listing 14-13 is to be modified as in Listing 14-14.

Listing 14-14 - Partials Can Be Used As Additional Columns — Use the _ Prefix

list:
  display:        [id, _article_link, created_at]

For this to work, an _article_link.php partial must be created in the modules/comment/templates/ directory, as in Listing 14-15.

Listing 14-15 - Example Partial for the list View, in modules/comment/templates/_article_link.php

<?php echo link_to($comment->getArticle()->getTitle(), 'article/edit?id='.$comment->getArticleId()) ?>

Notice that the partial template of a partial field has access to the current object through a variable named by the class ($comment in this example). For instance, for a module built for a class called UserGroup, the partial will have access to the current object through the $user_group variable.

The result is the same as in Figure 14-11, except that the layer separation is respected. If you get used to respecting the layer separation, you will end up with more maintainable applications.

If you need to customize the parameters of a partial field, do the same as for a normal field, under the field key. Just don't include the leading underscore (_) in the key — see an example in Listing 14-16.

Listing 14-16 - Partial Field Properties Can Be Customized Under the fields Key

fields:
  article_link:   { name: Article }

If your partial becomes crowded with logic, you'll probably want to replace it with a component. Change the _ prefix to ~ and you can define a component field, as you can see in Listing 14-17.

Listing 14-17 - Components Can Be Used As Additional Columns — Use the ~ Prefix

...
list:
  display:        [id, ~article_link, created_at]

In the generated template, this will result by a call to the articleLink component of the current module.

Note Custom and partial fields can be used in the list view, the edit view, and for filters. If you use the same partial for several views, the context ('list', 'edit', or 'filter') is stored in the $type variable.

14.3.2. View Customization

To change the edit and list views' appearance, you could be tempted to alter the templates. But because they are automatically generated, doing so isn't a very good idea. Instead, you should use the generator.yml configuration file, because it can do almost everything that you need without sacrificing modularity.

14.3.2.1. Changing the View Title ###

In addition to a custom set of fields, the list and edit pages can have a custom page title. For instance, if you want to customize the title of the article views, do as in Listing 14-18. The resulting edit view is illustrated in Figure 14-12.

Listing 14-18 - Setting a Custom Title for Each View, in backend/modules/article/config/generator.yml

list:
  title:          List of Articles
  ...

edit:
  title:          Body of article %%title%%
  display:        [content]
Custom title in the edit view of the article module

Figure 14.9 Custom title in the edit view of the article module

As the default titles use the class name, they are often good enough — provided that your model uses explicit class names.

Tip In the string values of generator.yml, the value of a field can be accessed via the name of the field surrounded by %%.

14.3.2.2. Adding Tooltips ###

In the list and edit views, you can add tooltips to help describe the fields that are displayed. For instance, to add a tooltip to the article_id field of the edit view of the comment module, add a help property in the fields definition as in Listing 14-19. The result is shown in Figure 14-13.

Listing 14-19 - Setting a Tooltip in the edit View, in modules/comment/config/generator.yml

edit:
  fields:
    ...
    article_id:   { help: The current comment relates to this article }
Tooltip in the edit view of the comment module

Figure 14.10 Tooltip in the edit view of the comment module

In the list view, tooltips are displayed in the column header; in the edit view, they appear under the input.

14.3.2.3. Modifying the Date Format ###

Dates can be displayed using a custom format as soon as you use the date_format param, as demonstrated in Listing 14-20.

Listing 14-20 - Formatting a Date in the list View

list:
  fields:
    created_at:         { name: Published, params: date_format='dd/MM' }

It takes the same format parameter as the format_date() helper described in the previous chapter.

14.3.3. List View-Specific Customization

The list view can display the details of a record in a tabular way, or with all the details stacked in one line. It also contains filters, pagination, and sorting features. These features can be altered by configuration, as described in the next sections.

14.3.3.1. Changing the Layout ###

By default, the hyperlink between the list view and the edit view is borne by the primary key column. If you refer back to Figure 14-11, you will see that the id column in the comment list not only shows the primary key of each comment, but also provides a hyperlink allowing users to access the edit view.

If you prefer the hyperlink to the detail of the record to appear on another column, prefix the column name by an equal sign (=) in the display key. Listing 14-21 shows how to remove the id from the displayed fields of the comment list and to put the hyperlink on the content field instead. Check Figure 14-14 for a screenshot.

Listing 14-21 - Moving the Hyperlink for the edit View in the list View, in modules/comment/config/generator.yml

list:
  display:    [article_link, =content]
Moving the link to the edit view on another column, in the list view of the comment module

Figure 14.11 Moving the link to the edit view on another column, in the list view of the comment module

By default, the list view uses the tabular layout, where the fields appear as columns, as shown previously. But you can also use the stacked layout and concatenate the fields into a single string that expands on the full length of the table. If you choose the stacked layout, you must set in the params key the pattern defining the value of each line of the list. For instance, Listing 14-22 defines a stacked layout for the list view of the comment module. The result appears in Figure 14-15.

Listing 14-22 - Using a stacked Layout in the list View, in modules/comment/config/generator.yml

list:
  layout:  stacked
  params:  |
    %%=content%% <br />
    (sent by %%author%% on %%created_at%% about %%article_link%%)
  display:  [created_at, author, content]
Stacked layout in the list view of the comment module

Figure 14.12 Stacked layout in the list view of the comment module

Notice that a tabular layout expects an array of fields under the display key, but a stacked layout uses the params key for the HTML code generated for each record. However, the display array is still used in a stacked layout to determine which column headers are available for the interactive sorting.

14.3.3.2. Filtering the Results ###

In a list view, you can add a set of filter interactions. With these filters, users can both display fewer results and get to the ones they want faster. Configure the filters under the filters key, with an array of field names. For instance, add a filter on the article_id, author, and created_at fields to the comment list view, as in Listing 14-23, to display a filter box similar to the one in Figure 14-16. You will need to add a __toString() method to the Article class (returning, for instance, the article title) for this to work.

Listing 14-23 - Setting the Filters in the list View, in modules/comment/config/generator.yml

list:
  filters: [article_id, author, created_at]
  layout:  stacked
  params:  |
    %%=content%% <br />
    (sent by %%author%% on %%created_at%% about %%article_link%%)
  display:  [created_at, author, content]
Filters in the list view of the comment module

Figure 14.13 Filters in the list view of the comment module

The filters displayed by symfony depend on the column type:

  • For text columns (like the author field in the comment module), the filter is a text input allowing text-based search with wildcards (*).
  • For foreign keys (like the article_id field in the comment module), the filter is a drop-down list of the records of the related table. As for the regular object_select_tag(), the options of the drop-down list are the ones returned by the __toString() method of the related class.
  • For date columns (like the created_at field in the comment module), the filter is a pair of rich date tags (text fields filled by calendar widgets), allowing the selection of a time interval.
  • For Boolean columns, the filter is a drop-down list having true, false, and true or false options — the last value reinitializes the filter.

Just like you use partial fields in lists, you can also use partial filters to create a filter that symfony doesn't handle on its own. For instance, imagine a state field that may contain only two values (open and closed), but for some reason you store those values directly in the field instead of using a table relation. A simple filter on this field (of type string) would be a text-based search, but what you want is probably a drop-down list of values. That's easy to achieve with a partial filter. See Listing 14-24 for an example implementation.

Listing 14-24 - Using a Partial Filter

// Define the partial, in templates/_state.php
<?php echo select_tag('filters[state]', options_for_select(array(
  '' => '',
  'open' => 'open',
  'closed' => 'closed',
), isset($filters['state']) ? $filters['state'] : '')) ?>

// Add the partial filter in the filter list, in config/generator.yml
    list:
      filters:        [date, _state]

Notice that the partial has access to a $filters variable, which is useful to get the current value of the filter.

There is one last option that can be very useful for looking for empty values. Imagine that you want to filter the list of comments to display only the ones that have no author. The problem is that if you leave the author filter empty, it will be ignored. The solution is to set the filter_is_empty field setting to true, as in Listing 14-25, and the filter will display an additional check box, which will allow you to look for empty values, as illustrated in Figure 14-17.

Listing 14-25 - Adding Filtering of Empty Values on the author Field in the list View

list:
  fields:
    author:   { filter_is_empty: true }
  filters:    [article_id, author, created_at]
Allowing the filtering of empty author values

Figure 14.14 Allowing the filtering of empty author values

14.3.3.3. Sorting the List ###

In a list view, the table headers are hyperlinks that can be used to reorder the list, as shown in Figure 14-18. These headers are displayed both in the tabular and stacked layouts. Clicking these links reloads the page with a sort parameter that rearranges the list order accordingly.

Table headers of the list view are sort controls

Figure 14.15 Table headers of the list view are sort controls

You can reuse the syntax to point to a list directly sorted according to a column:

<?php echo link_to('Comment list by date', 'comment/list?sort=created_at&type=desc' ) ?>

You can also define a default sort order for the list view directly in the generator.yml file. The syntax follows the example given in Listing 14-26.

Listing 14-26 - Setting a Default Sort Field in the list View

list:
  sort:   created_at
  # Alternative syntax, to specify a sort order
  sort:   [created_at, desc]

Note Only the fields that correspond to an actual column are transformed into sort controls — not the custom or partial fields.

14.3.3.4. Customizing the Pagination ###

The generated administration effectively deals with even large tables, because the list view uses pagination by default. When the actual number of rows in a table exceeds the number of maximum rows per page, pagination controls appear at the bottom of the list. For instance, Figure 14-19 shows the list of comments with six test comments in the table but a limit of five comments displayed per page. Pagination ensures a good performance, because only the displayed rows are effectively retrieved from the database, and a good usability, because even tables with millions of rows can be managed by an administration module.

Pagination controls appear on long lists

Figure 14.16 Pagination controls appear on long lists

You can customize the number of records to be displayed in each page with the max_per_page parameter:

list:
  max_per_page:   5

14.3.3.5. Using a Join to Speed Up Page Delivery ###

By default, the administration generator uses a simple doSelect() to retrieve a list of records. But, if you use related objects in the list, the number of database queries required to display the list may rapidly increase. For instance, if you want to display the name of the article in a list of comments, an additional query is required for each post in the list to retrieve the related Article object. So you may want to force the pager to use a doSelectJoinXXX() method to optimize the number of queries. This can be specified with the peer_method parameter.

list:
  peer_method:   doSelectJoinArticle

Chapter 18 explains the concept of Join more extensively.

14.3.4. Edit View-Specific Customization

In an edit view, the user can modify the value of each column for a given record. Symfony determines the type of input to display according to the data type of the column. It then generates an object_*_tag() helper, and passes that helper the object and the property to edit. For instance, if the article edit view configuration stipulates that the user can edit the title field:

edit:
  display: [title, ...]

then the edit page will display a regular text input tag to edit the title because this column is defined as a varchar type in the schema.

<?php echo object_input_tag($article, 'getTitle') ?>

14.3.4.1. Changing the Input Type ###

The default type-to-field conversion rules are as follows:

  • A column defined as integer, float, char, varchar(size) appears in the edit view as an object_input_tag().
  • A column defined as longvarchar appears as an object_textarea_tag().
  • A foreign key column appears as an object_select_tag().
  • A column defined as boolean appears as an object_checkbox_tag().
  • A column defined as a timestamp or date appears as an object_input_date_tag().

You may want to override these rules to specify a custom input type for a given field. To that extent, set the type parameter in the fields definition to a specific form helper name. As for the options of the generated object_*_tag(), you can change them with the params parameter. See an example in Listing 14-27.

Listing 14-27 - Setting a Custom Input Type and Params for the edit View

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Comment
    theme:            default

    edit:
      fields:
                      ## Drop the input, just display plain text
        id:           { type: plain }
                      ## The input is not editable
        author:       { params: disabled=true }
                      ## The input is a textarea (object_textarea_tag)
        content:      { type: textarea_tag, params: rich=true css=user.css tinymce_options=width:330 }
                      ## The input is a select (object_select_tag)
        article_id:   { params: include_custom=Choose an article }
         ...

The params parameters are passed as options to the generated object_*_tag(). For instance, the params definition for the preceding article_id will produce in the template the following:

<?php echo object_select_tag($comment, 'getArticleId', 'related_class=Article', 'include_custom=Choose an article') ?>

This means that all the options usually available in the form helpers can be customized in an edit view.

14.3.4.2. Handling Partial Fields ###

Partial fields can be used in edit views just like in list views. The difference is that you have to handle by hand, in the action, the update of the column according to the value of the request parameter sent by the partial field. Symfony knows how to handle the normal fields (corresponding to actual columns), but can't guess how to handle the inputs you may include in partial fields.

For instance, imagine an administration module for a User class where the available fields are id, nickname, and password. The site administrator must be able to change the password of a user upon request, but the edit view must not display the value of the password field for security reasons. Instead, the form should display an empty password input that the site administrator can fill to change the value. The generator settings for such an edit view are then similar to Listing 14-28.

Listing 14-28 - Including a Partial Field in the edit View

edit:
  display:        [id, nickname, _newpassword]
  fields:
    newpassword:  { name: Password, help: Enter a password to change it, leave the field blank to keep the current one }

The templates/_newpassword.php partial contains something like this:

<?php echo input_password_tag('newpassword', '') ?>

Notice that this partial uses a simple form helper, not an object form helper, since it is not desirable to retrieve the password value from the current User object to populate the form input — which could disclose the user password.

Now, in order to use the value from this control to update the object in the action, you need to extend the updateUserFromRequest() method in the action. To do that, create a method with the same name in the action class file with the custom behavior for the input of the partial field, as in Listing 14-29.

Listing 14-29 - Handling a Partial Field in the Action, in modules/user/actions/actions.class.php

class userActions extends autouserActions
{
  protected function updateUserFromRequest()
  {
    // Handle the input of the partial field
    $password = $this->getRequest()->getParameter('newpassword');

    if ($password)
    {
      $this->user->setPassword($password);
    }

    // Let symfony handle the other fields
    parent::updateUserFromRequest();
  }
}

Note In the real world, a user/edit view usually contains two password fields, the second having to match the first one to avoid typing mistakes. In practice, as you saw in Chapter 10, this is done via a validator. The administration-generated modules benefit from this mechanism just like regular modules.

14.3.5. Dealing with Foreign Keys

If your schema defines table relationships, the generated administration modules take advantage of it and offer even more automated controls, thus greatly simplifying the relationship management.

14.3.5.1. One-to-Many Relationships ###

The 1-n table relationships are taken care of by the administration generator. As is depicted by Figure 14-1 earlier, the blog_comment table is related to the blog_article table through the article_id field. If you initiate the module of the Comment class with the administration generator, the comment/edit action will automatically display the article_id as a drop-down list showing the IDs of the available records of the blog_article table (check again Figure 14-9 for an illustration).

In addition, if you define a __toString() method in the Article class, the text of the drop-down options use it instead of the primary keys.

If you need to display the list of comments related to an article in the article module (n-1 relationship), you will need to customize the module a little by way of a partial field.

14.3.5.2. Many-to-Many Relationships ###

Symfony also takes care of n-n table relationships, but since you can't define them in the schema, you need to add a few parameters to the generator.yml file.

The implementation of many-to-many relationships requires an intermediate table. For instance, if there is an n-n relation between a blog_article and a blog_author table (an article can be written by more than one author and, obviously, an author can write more than one article), your database will always end up with a table called blog_article_author or similar, as in Figure 14-20.

Using a "through class" to implement many-to-many relationships

Figure 14.17 Using a "through class" to implement many-to-many relationships

The model then has a class called ArticleAuthor, and this is the only thing that the administration generator needs — but you have to pass it as a through_class parameter of the field.

For instance, in a generated module based on the Article class, you can add a field to create new n-n associations with the Author class if you write generator.yml as in Listing 14-30.

Listing 14-30 - Handling Many-to-Many Relationships with a through_class Parameter

edit:
  fields:
    article_author: { type: admin_double_list, params: through_class=ArticleAuthor }

Such a field handles links between existing objects, so a regular drop-down list is not enough. You must use a special type of input for that. Symfony offers three widgets to help relate members of two lists (illustrated in Figure 14-21):

  • An admin_double_list is a set of two expanded select controls, together with buttons to switch elements from the first list (available elements) to the second (selected elements).
  • An admin_select_list is an expanded select control in which you can select many elements.
  • An admin_check_list is a list of check box tags.
Available controls for many-to-many relationships

Figure 14.18 Available controls for many-to-many relationships

14.3.6. Adding Interactions

Administration modules allow users to perform the usual CRUD operations, but you can also add your own interactions or restrict the possible interactions for a view. For instance, the interaction definition shown in Listing 14-31 gives access to all the default CRUD actions on the article module.

Listing 14-31 - Defining Interactions for Each View, in backend/modules/article/config/generator.yml

list:
  title:          List of Articles
  object_actions:
    _edit:         ~
    _delete:       ~
  batch_actions:
    _delete:       ~
  actions:
    _create:       ~

edit:
  title:          Body of article %%title%%
  actions:
    _list:         ~
    _save:         ~
    _save_and_add: ~
    _delete:       ~

In a list view, there are three action settings: the actions available for every object (object_actions), the actions available for a selection of objects (batch_actions), and actions available for the whole page (actions). The list interactions defined in Listing 14-31 render like in Figure 14-22. Each line shows one button to edit the record and one to delete it, plus one checkbox on each line to delete a selection of records. At the bottom of the list, a button allows the creation of a new record.

Interactions in the list view

Figure 14.19 Interactions in the list view

In an edit view, as there is only one record edited at a time, there is only one set of actions to define (under actions). The edit interactions defined in Listing 14-31 render like in Figure 14-23. Both the save and the save_and_add actions save the current edits in the records, the difference being that the save action displays the edit view on the current record after saving, while the save_and_add action displays an empty edit view to add another record. The save_and_add action is a shortcut that you will find very useful when adding many records in rapid succession. As for the position of the delete action, it is separated from the other buttons so that users don't click it by mistake.

The interaction names starting with an underscore (_) tell symfony to use the default icon and action corresponding to these interactions. The administration generator understands _edit, _delete, _list, _save, _save_and_add, and _create.

Interactions in the edit view

Figure 14.20 Interactions in the edit view

But you can also add a custom interaction, in which case you must specify a name starting with no underscore, and a target action in the current module, as in Listing 14-32.

Listing 14-32 - Defining a Custom Interaction

list:
  title:          List of Articles
  object_actions:
    _edit:        -
    _delete:      -
    addcomment:   { name: Add a comment, action: addComment, icon: backend/addcomment.png }

Each article in the list will now show the web/images/addcomment.png button, as shown in Figure 14-24. Clicking it triggers a call to the addComment action in the current module. The primary key of the current object is automatically added to the request parameters.

Custom interaction in the list view

Figure 14.21 Custom interaction in the list view

The addComment action can be implemented as in Listing 14-33.

Listing 14-33 - Implementing the Custom Interaction Action, in actions/actions.class.php

public function executeAddComment($request)
{
  $comment = new Comment();
  $comment->setArticleId($request->getParameter('id'));
  $comment->save();

  $this->redirect('comment/edit?id='.$comment->getId());
}

Batch actions receive an array of the primary keys of the selected records in the sf_admin_batch_selection request parameter.

One last word about actions: If you want to suppress completely the actions for one category, use an empty list, as in Listing 14-34.

Listing 14-34 - Removing All Actions in the list View

list:
  title:          List of Articles
  actions:        {}

14.3.7. Form Validation

If you take a look at the generated _edit_form.php template in your project cache/ directory, you will see that the form fields use a special naming convention. In a generated edit view, the input names result from the concatenation of the underscore-syntaxed model class name and the field name between angle brackets.

For instance, if the edit view for the Article class has a title field, the template will look like Listing 14-35 and the field will be identified as article[title].

Listing 14-35 - Syntax of the Generated Input Names

# generator.yml
generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default
    edit:
      display: [title]

// Resulting _edit_form.php template
<?php echo object_input_tag($article, 'getTitle', array('control_name' => 'article[title]')) ?>

// Resulting HTML
<input type="text" name="article[title]" id="article_title" value="My Title" />

This has plenty of advantages during the internal form-handling process. However, as explained in Chapter 10, it makes the form validation configuration a bit trickier, so you have to change square brackets, [ ], to curly braces, { }, in the fields definition. Also, when using a field name as a parameter for a validator, you should use the name as it appears in the generated HTML code (that is, with the square brackets, but between quotes). Refer to Listing 14-36 for a detail of the special validator syntax for generated forms.

Listing 14-36 - Validator File Syntax for Administration-Generated Forms

## Replace square brackets by curly brackets in the fields list
fields:
  article{title}:
    required:
      msg: You must provide a title
    ## For validator parameters, use the original field name between quotes
    sfCompareValidator:
      check:        "user[newpassword]"
      compare_error: The password confirmation does not match the password.

14.3.8. Restricting User Actions Using Credentials

For a given administration module, the available fields and interactions can vary according to the credentials of the logged user (refer to Chapter 6 for a description of symfony's security features).

The fields in the generator can take a credentials parameter into account so as to appear only to users who have the proper credential. This works for the list view and the edit view. Additionally, the generator can also hide interactions according to credentials. Listing 14-37 demonstrates these features.

Listing 14-37 - Using Credentials in generator.yml

## The id column is displayed only for users with the admin credential
    list:
      title:          List of Articles
      layout:         tabular
      display:        [id, =title, content, nb_comments]
      fields:
        id:           { credentials: [admin] }

## The addcomment interaction is restricted to the users with the admin credential
    list:
      title:          List of Articles
      object_actions:
        _edit:        -
        _delete:      -
        addcomment:   { credentials: [admin], name: Add a comment, action: addComment, icon: backend/addcomment.png }