You must first sign up to be able to contribute.


Revision 30059, 13.0 kB (checked in by francois, 8 years ago)

[sfPropel15Plugin] Added max_additions options to mergeRelation() and embedRelation()

  • Property svn:executable set to *
1 Form Extensions
2 ===============
4 The `sfPropel15Plugin` provides two widgets and three validators, to help build and validate forms bound to a Model object.
6 `sfWidgetPropelChoice` and `sfValidatorPropelChoice`
7 ----------------------------------------------------
9 Editing a foreign key columns is often a matter of choosing the related object to relate. For instance, editing the `author_id` field of an `Article` model means choosing an element in the list of existing Authors. `sfPropel15Plugin` provides an extension of the `sfWidgetFormChoice` class that takes care of populating the list of options based on a related table. It is called `sfWidgetPropelChoice`, and is associated witha validator called `sfValidatorPropelChoice`.
11 ### Generated Configuration
13 Most of the time, the configuration of this widget and validator is already done in the generated forms and filter forms. Using the previous example model, Propel would generate the following Base form:
15     [php]
16     abstract class BaseArticleForm extends BaseFormPropel
17     {
18       public function setup()
19       {
20         // ...
22         $this->setWidgets(array(
23           // ...
24           'author_id' => new sfWidgetFormPropelChoice(array(
25             'model' => 'Author',
26             'add_empty' => true)
27           ),
28         ));
30         $this->setValidators(array(
31           // ...
32           'author_id' => new sfValidatorPropelChoice(array(
33             'model' => 'Author',
34             'column' => 'id',
35             'required' => false)
36           ),
37         ));
38       }
39     }
41 Based on the `model` setting, the plugin generates the list of possible choices for the widget and the validator.
43 ### Additional Query Methods
45 You can set the widget to execute additional query methods on the related Model Query object. For instance, if a `Section` model uses the `nested_set` behavior, you probably want to display a section selection widget in hierarchical order. This is easily achived by executing the `SectionQuery::orderByBranch()` query method, and you can register it as follows:
47     [php]
48     class ContentForm extends BaseContentForm
49     {
50       public function configure()
51       {
52         $this->widgetSchema['section'] = new sfWidgetFormPropelChoice(array(
53           'model'         => 'Section',
54           'query_methods' => array('orderByBranch')
55           'add_empty'     => true,
56         ));
57       }
58     }
61 You can also enable the `query_method` option on an existing widget. For instance, to display only the list of active authors, customize the form as follows:
63     [php]
64     class ArticleForm extends BaseArticleForm
65     {
66       public function configure()
67       {
68         $this->widgetSchema['author_id']->setOption('query_methods', array('active'));
69       }
70     }
72     class ArticleQuery extends BaseArticleQuery
73     {
74       public function active()
75       {
76         return $this->filterByIsActive(true);
77       }
78     }
80 Of course, to allow the validation of the user's choice, the `query_methods` option is also available in the `sfValidatorPropelChoice` validator. Always remember to apply the same filters in the validator as in the widget.
82 So if you display a selection of items using a query method, you can validate this selection, too:
84    [php]
85    class ContentForm extends BaseContentForm
86    {
87      public function configure()
88      {
89        $this->widgetSchema['section']->setOption('query_methods', array('published'));
90        $this->validatorSchema['section']->setOption('query_methods', array('published'));
91      }
92    }
94 ### Using A Custom Query Object
96 Alternatively, build the query yourself in the form, and pass it to the widget in the `criteria` option:
98     [php]
99     class ArticleForm extends BaseArticleForm
100     {
101       public function configure()
102       {
103         $query = ArticleQuery::create()->filterByIsActive(true);
104         $this->widgetSchema['author_id']->setOption('criteria', $query);
105       }
106     }
108 The `criteria` option is also available in `sfValidatorPropelChoice`.
110 ### Full Options List
112 The `sfWidgetFormPropelChoice` widget supports the following options:
114 * `model`: The model class (required)
115 * `add_empty`: Whether to add a first empty value or not (false by default). If the option is not a Boolean, the value will be used as the text value
116 * `method`: The method to use to display object values (__toString by default)
117 * `key_method`: The method to use to display the object keys (getPrimaryKey by default)
118 * `order_by`: An array composed of two fields:
119   * The column to order by the results (must be in the PhpName format)
120   * asc or desc
121 * `query_methods`: An array of method names listing the methods to execute on the model's query object
122 * `criteria`: A criteria to use when retrieving objects
123 * `connection`: The Propel connection to use (null by default)
124 * `multiple`: true if the select tag must allow multiple selections
126 The `sfValidatorPropelChoice` validator accepts almost the same options:
128 * `model`: The model class (required)
129 * `query_methods`: An array of method names listing the methods to execute on the model's query object
130 * `criteria`: A criteria to use when retrieving objects
131 * `column`: The column name (null by default which means we use the primary key) must be in field name format
132 * `connection`: The Propel connection to use (null by default)
133 * `multiple`: true if the select tag must allow multiple selections
134 * `min`: The minimum number of values that need to be selected (this option is only active if multiple is true)
135 * `max`: The maximum number of values that need to be selected (this option is only active if multiple is true)
137 `sfValidatorPropelUnique`
138 -------------------------
140 In a blog application, two articles can not have the same slug; to ensure this constraint, the schema definition features a uniqueness constraint. This constraint on the database level is reflected in the ArticleForm form using the `sfValidatorPropelUnique` validator. This validator can check the uniqueness of any form field. It is helpful among other things to check the uniqueness of an email address of a login for instance. The next listing shows how to use it in the ArticleForm  form.
142     [php]
143     class ArticleForm extends BaseArticleForm
144     {
145       public function configure()
146       {
147         // ...
149         $this->validatorSchema->setPostValidator(
150           new sfValidatorPropelUnique(array(
151             'model' => 'Article',
152             'column' => array('slug')))
153         );
154       }
155     }
157 The sfValidatorPropelUnique validator is a postValidator running on the whole data after the individual validation of each field. In order to validate the slug uniqueness, the validator must be able to access, not only the slug value, but also the value of the primary key(s). Validation rules are indeed different throughout the creation and the edition since the slug can stay the same during the update of an article.
159 This validator supports the following options:
161 * `model`: The model class (required)
162 * `column`: The unique column name in Propel field name format (required). If the uniquess is for several columns, you can pass an array of fiel names
163 * `query_methods`: An array of method names listing the methods to executeon the model's query object
164 * `field`: Field name used by the form, other than the column name
165 * `primary_key`: The primary key column name in Propel field name format (optional, will be introspected if not provided). You can also pass an array if the table has several primary keys
166 * `connection`: The Propel connection to use (null by default)
167 * `throw_global_error`: Whether to throw a global error (false by default) or an error tied to the first field related to the column option array
169 `sfWidgetFormPlain` and `sfValidatorSchemaRemove`
170 -------------------------------------------------
172 To display a field without any possiblity to change its value, no need to use a partial field. Just use the `sfWidgetFormPlain` widget to display the value in a div. Don't forget to disable the validator on that field, too, using the `sfValidatorPass` validator.
174 But symfony expects to receive all form fields for binding, including plain fields. If the field is not present in the request, symfony uses a null value, which may erase the data in the column you want to display. To avoid erasing data, use the new `sfValidatorSchemaRemove` to remove plain fields from the binding process. This is a post validator, and it expects an array of field names in the `fields` option.
176 Here is an example for `created_at` and `updated_at` columns, that you may want to display without allowing their edition:
178     [php]
179     class ArticleForm extends BaseArticleForm
180     {
181       public function configure()
182       {
183         // ...
184         $this->setWidget('created_at', new sfWidgetFormPlain());
185         $this->setWidget('updated_at', new sfWidgetFormPlain());
186         $this->setValidator('created_at', new sfValidatorPass(array('required' => false)));
187         $this->setValidator('updated_at', new sfValidatorPass(array('required' => false)));
188         $this->mergePostValidator(
189           new sfValidatorSchemaRemove(array('fields' => array('created_at', 'updated_at')))
190         );
191       }
192     }
194 **Tip**: If you use the admin generator, setting a field with `type: plain` produces the same effect, only in much less code.
196     [yaml]
197     edit:
198       fields:
199         created_at: { type: plain }
200         updated_at: { type: plain }
202 `sfFormPropelCollection`
203 ------------------------
205 If you need to build a form based on a collection of objects rather than on a single object, then the `sfFormPropelCollection` class will help you. To create such a form, just pass a PropelObjectcollection instance to its constructor, and you can use the form as a regular Propel object form:
207     [php]
208     $collection = new PropelObjectCollection();
209     $collection->setModel('Book');
210     $collection[]= new Book();
211     $collection[]= new Book();
212     $collection[]= new Book();
213     $form = new sfFormPropelCollection($collection);
214     echo $form; // displays a list of 3 BookForms, bound to each element in the collection
216 Embedding A Relation Form
217 -------------------------
219 Since one-to-many relationships return `PropelCollection` objects, the ability to create a collection form, added to the ability to merge two forms together, makes the edition of related objects very straightforward.
221 `sfPropelForm` provides a method called `embedRelation($relationName)`, which fetches a collection for a given relation, creates a `sfFormPropelCollection` instance based on the collection, and embeds this form into the main form. This allows,for instance, to edit an author together with all its books:
223     [php]
224     class ArticleForm extends BaseArticleForm
225     {
226       public function configure()
227       {
228         $this->embedRelation('Book');
229       }
230     }
232 Now the Article form displays the list of related books for each author, together with controls to add or remove Books for a given Author. No need to add code to the form object, it just works.
234 **Tip**: `sfPropelForm` also supports `mergeRelation()`, which merges the individual forms from the colleciton form into the parent form. As for embedded forms, merged relation forms support addition and removal of related objects.
236 `embedRelation()` offers many options to customize the embedded relation form:
238 * `title`: The title of the collection form once embedded. Defaults to the relation name.
239 * `embedded_form_class`: The class name of the forms to embed. Uses the model name by default (a form based on a collection of Book objects embeds BookForm objects).
240 * `collection_form_class`: Class of the collection form to return. Defaults to sfFormPropelCollection.
241 * `hide_on_new`: If true, the relation form does not appear for new objects. Defaults to false.
242 * `add_empty`: Whether to allow the user to add new objects to the collection. Defaults to true.
243 * `add_delete`: Whether to add a delete widget for each object. Defaults to true.
244 * `remove_fields`: The list of fields to remove from the embedded object forms
245 * `item_pattern`: The pattern used to name each embedded form. Defaults to '%index%'.
247 If `add_empty` is set to `true`, the following additional options are available:
249 * `empty_label`: The label of the empty form. Defaults to 'new' + the relation name.
250 * `add_link`: The text of the JavaScript link that displays the empty form. Defaults to `Ann new`
251 * `max_additions`: The max number of additions accepted on the client side. Defaults to 0 (no limit)
253 If `add_delete` is set to `true`, the following additional options are available:
255 * `delete_name`: Name of the delete widget. Defaults to 'delete'.
256 * `delete_widget`: Optional delete widget object. If left null, uses a `sfWidgetFormDelete` instance, which is a checkbox widget with a Javascript confirmation.
257 * `alert_text`: The text of the Javascript alert to show.
258 * `hide_parent`: Whether to hide the deleted form when clicking the checkbox. Defaults to true.
259 * `parent_level`: The number of times parentNode must be called to reach the parent to hide. As a widget doesn't know if it's merged or embedded, this setting allows the JavaScript code used to hide the parent to find it. Recommended values: 6 for embedded form (default), 7 for merged form.
Note: See TracBrowser for help on using the browser.