forked from yiisoft/yii
-
Notifications
You must be signed in to change notification settings - Fork 0
/
form.model.txt
350 lines (281 loc) · 11.5 KB
/
form.model.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
Creating Model
==============
Before writing the HTML code needed by a form, we should decide what kind
of data we are expecting from end users and what rules these data should
comply with. A model class can be used to record these information. A
model, as defined in the [Model](/doc/guide/basics.model) subsection, is
the central place for keeping user inputs and validating them.
Depending on how we make use of the user input, we can create two types of
model. If the user input is collected, used and then discarded, we would
create a [form model](/doc/guide/basics.model); if the user input is
collected and saved into database, we would use an [active
record](/doc/guide/database.ar) instead. Both types of model share the same
base class [CModel] which defines the common interface needed by form.
> Note: We are mainly using form models in the examples of this section.
However, the same can also be applied to [active
record](/doc/guide/database.ar) models.
Defining Model Class
--------------------
Below we create a `LoginForm` model class used to collect user input on a
login page. Because the login information is only used to authenticate the
user and does not need to be saved, we create `LoginForm` as a form model.
~~~
[php]
class LoginForm extends CFormModel
{
public $username;
public $password;
public $rememberMe=false;
}
~~~
Three attributes are declared in `LoginForm`: `$username`, `$password` and
`$rememberMe`. They are used to keep the user-entered username and
password, and the option whether the user wants to remember his login.
Because `$rememberMe` has a default value `false`, the corresponding option
when initially displayed in the login form will be unchecked.
> Info: Instead of calling these member variables properties, we use the
name *attributes* to differentiate them from normal properties. An
attribute is a property that is mainly used to store data coming from user
input or database.
Declaring Validation Rules
--------------------------
Once a user submits his inputs and the model gets populated, we need to
make sure the inputs are valid before using them. This is done by
performing validation of the inputs against a set of rules. We specify the
validation rules in the `rules()` method which should return an array of
rule configurations.
~~~
[php]
class LoginForm extends CFormModel
{
public $username;
public $password;
public $rememberMe=false;
public function rules()
{
return array(
array('username, password', 'required'),
array('password', 'authenticate'),
array('rememberMe', 'safe'),
);
}
public function authenticate($attribute,$params)
{
if(!$this->hasErrors()) // we only want to authenticate when no input errors
{
$identity=new UserIdentity($this->username,$this->password);
if($identity->authenticate())
{
$duration=$this->rememberMe ? 3600*24*30 : 0; // 30 days
Yii::app()->user->login($identity,$duration);
}
else
$this->addError('password','Incorrect password.');
}
}
}
~~~
The above code specifies that `username` and `password` are both required,
`password` should be authenticated, and `rememberMe` is safe to be massively
assigned (to be explained in the next subsection).
Each rule returned by `rules()` must be of the following format:
~~~
[php]
array('AttributeList', 'Validator', 'on'=>'ScenarioList', ...additional options)
~~~
where `AttributeList` is a string of comma-separated attribute names which
need to be validated according to the rule; `Validator` specifies what kind of
validation should be performed; the `on` parameter is optional which specifies
a list of scenarios where the rule should be applied; and additional options
are name-value pairs which are used to initialize the corresponding validator's
property values.
There are three ways to specify `Validator` in a validation rule. First,
`Validator` can be the name of a method in the model class, like
`authenticate` in the above example. The validator method must be of the
following signature:
~~~
[php]
/**
* @param string the name of the attribute to be validated
* @param array options specified in the validation rule
*/
public function ValidatorName($attribute,$params) { ... }
~~~
Second, `Validator` can be the name of a validator class. When the rule is
applied, an instance of the validator class will be created to perform the
actual validation. The additional options in the rule are used to
initialize the instance's attribute values. A validator class must extend
from [CValidator].
> Note: When specifying rules for an active record model, we can use a
special option named `on`. The option can be either `'insert'` or
`'update'` so that the rule is applied only when inserting or updating the
record, respectively. If not set, the rule would be applied in both cases
when `save()` is called.
Third, `Validator` can be a predefined alias to a validator class. In the
above example, the name `required` is the alias to [CRequiredValidator]
which ensures the attribute value being validated is not empty. Below is
the complete list of predefined validator aliases:
- `captcha`: alias of [CCaptchaValidator], ensuring the attribute is
equal to the verification code displayed in a
[CAPTCHA](http://en.wikipedia.org/wiki/Captcha).
- `compare`: alias of [CCompareValidator], ensuring the attribute is
equal to another attribute or constant.
- `email`: alias of [CEmailValidator], ensuring the attribute is a
valid email address.
- `file`: alias of [CFileValidator], ensuring the attribute contains
the name of an uploaded file.
- `filter`: alias of [CFilterValidator], transforming the attribute
with a filter.
- `in`: alias of [CRangeValidator], ensuring the data is among a
pre-specified list of values.
- `length`: alias of [CStringValidator], ensuring the length of the
data is within certain range.
- `match`: alias of [CRegularExpressionValidator], ensuring the data
matches a regular expression.
- `numerical`: alias of [CNumberValidator], ensuring the data is a
valid number.
- `required`: alias of [CRequiredValidator], ensuring the attribute is
not empty.
- `type`: alias of [CTypeValidator], ensuring the attribute is of
specific data type.
- `unique`: alias of [CUniqueValidator], ensuring the data is unique in
a database table column.
- `url`: alias of [CUrlValidator], ensuring the data is a valid URL.
Below we list some examples of using the predefined validators:
~~~
[php]
// username is required
array('username', 'required'),
// username must be between 3 and 12 characters
array('username', 'length', 'min'=>3, 'max'=>12),
// when in register scenario, password must match password2
array('password', 'compare', 'compareAttribute'=>'password2', 'on'=>'register'),
// when in login scenario, password must be authenticated
array('password', 'authenticate', 'on'=>'login'),
~~~
Securing Attribute Assignments
------------------------------
> Note: scenario-based attribute assignment has been available since version 1.0.2.
After a model instance is created, we often need to populate its
attributes with the data submitted by end-users. This can be done
conveniently using the following massive assignment:
~~~
[php]
$model=new LoginForm;
if(isset($_POST['LoginForm']))
$model->setAttributes($_POST['LoginForm'], 'login');
~~~
The last statement is a massive assignment which assigns every entry
in `$_POST['LoginForm']` to the corresponding model attribute in the
`login` scenario (specified as the second parameter). It is equivalent to
the following assignments:
~~~
[php]
foreach($_POST['LoginForm'] as $name=>$value)
{
if($name is a safe attribute)
$model->$name=$value;
}
~~~
The task of deciding whether a data entry is safe or not is based
the return value of a method named `safeAttributes` and the specified
scenario. By default, the method returns all public member variables
as safe attributes for [CFormModel], while it returns all table columns
except the primary key as safe attributes for [CActiveRecord]. We may
override this method to limit safe attributes according to scenarios.
For example, a user model may contain many attributes, but in `login`
scenario we only need to use `username` and `password` attributes.
We can specify this limit as follows:
~~~
[php]
public function safeAttributes()
{
return array(
parent::safeAttributes(),
'login' => 'username, password',
);
}
~~~
More accurately, the return value of the `safeAttributes` method should be
of the following structure:
~~~
[php]
array(
// these attributes can be massively assigned in any scenario
// that is not explicitly specified below
'attr1, attr2, ...',
*
// these attributes can be massively assigned only in scenario 1
'scenario1' => 'attr2, attr3, ...',
*
// these attributes can be massively assigned only in scenario 2
'scenario2' => 'attr1, attr3, ...',
)
~~~
If the model is not scenario-sensitive (i.e., it is only used
in one scenario, or all scenarios share the same set of safe attributes),
the return value can be simplified as a single string:
~~~
[php]
'attr1, attr2, ...'
~~~
For data entries that are not safe, we need to assign them to the corresponding
attributes using individual assign statements, like the following:
~~~
[php]
$model->permission='admin';
$model->id=1;
~~~
Triggering Validation
---------------------
Once a model is populated with user-submitted data, we can call [CModel::validate()]
to trigger the data validation process. The method returns a value
indicating whether the validation is successful or not. For [CActiveRecord] model,
validation may also be automatically triggered when we call its [CActiveRecord::save()]
method.
When we call [CModel::validate()], we may specify a scenario parameter.
Only the validation rules that apply to the specified scenario will be
executed. A validation rule applies to a scenario if the `on` option
of the rule is not set or contains the specified scenario name. If we do
not specify the scenario when calling [CModel::validate()], only those
rules whose `on` option is not set will be executed.
For example, we execute the following statement to perform the validation
when registering a user:
~~~
[php]
$model->validate('register');
~~~
We may declare the validation rules in the form model class as follows,
~~~
[php]
public function rules()
{
return array(
array('username, password', 'required'),
array('password_repeat', 'required', 'on'=>'register'),
array('password', 'compare', 'on'=>'register'),
);
}
~~~
As a result, the first rule will be applied in all scenarios, while the
next two rules will only be applied in the `register` scenario.
> Note: scenario-based validation has been available since version 1.0.1.
Retrieving Validation Errors
----------------------------
We can use [CModel::hasErrors()] to check if there is any validation
error, and if yes, we can use [CModel::getErrors()] to obtain the error
messages. Both methods can be used for all attributes or an individual
attribute.
Attribute Labels
----------------
When designing a form, we often need to display a label for each input
field. The label tells a user what kind of information he is expected to
enter into the field. Although we can hardcode a label in a view, it would
offer more flexibility and convenience if we specify it in the
corresponding model.
By default, [CModel] will simply return the name of an attribute as its
label. This can be customized by overriding the
[attributeLabels()|CModel::attributeLabels] method. As we will see in the
following subsections, specifying labels in the model allows us to create a
form more quickly and powerful.
<div class="revision">$Id$</div>