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 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; if the user input is collected and saved into database, we would use an active record 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 models.
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.
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.
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.
class LoginForm extends CFormModel
{
public $username;
public $password;
public $rememberMe=false;
public function rules()
{
return array(
array('username, password', 'required'),
array('password', 'authenticate'),
);
}
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.
Each rule returned by rules()
must be of the following format:
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:
/**
* @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 whensave()
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:
boolean
: alias of CBooleanValidator, ensuring the attribute has
a value that is either CBooleanValidator::trueValue or
CBooleanValidator::falseValue.
captcha
: alias of CCaptchaValidator, ensuring the attribute is
equal to the verification code displayed in a
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.
default
: alias of CDefaultValueValidator, assigning a default value
to the specified attributes.
exist
: alias of CExistValidator, ensuring the attribute value
can be found in the specified table column.
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:
// 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'),
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:
$model=new LoginForm;
$model->scenario='login';
if(isset($_POST['LoginForm']))
$model->attributes=$_POST['LoginForm'];
Note: The scenario property has been available since version 1.0.4. The massive assignment will take this property value to determine which attributes can be massively assigned. In version 1.0.2 and 1.0.3, we need to use the following way to perform massive assignment for a specific scenario:
$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. It is equivalent to the following assignments:
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 on
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:
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:
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:
'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:
$model->permission='admin';
$model->id=1;
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:
$model->scenario='register';
$model->validate();
Note: The scenario property has been available since version 1.0.4. The validation method will take this property value to determine which rules to be checked with. In version 1.0.2 and 1.0.3, we need to use the following way to perform scenario-based validation:
$model->validate('register');
We may declare the validation rules in the form model class as follows,
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.
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.
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() method. As we will see in the following subsections, specifying labels in the model allows us to create a form more quickly and powerful.
Signup or Login in order to comment.