翻了两篇,突然想起来,忘了提个醒。各位看官,我所翻的这个yii 指引手册早有中文版的,可以去找来看看。本人是闲来无聊,自己翻。
对于一个web工程来说,最主要的工作就是通过HTML表单来搜集用户的输入数据。除了设计表单,程序员还需要设置已有数据或者是默认值,验证用户输入,正确并人性化的输出错误提示信息,保存数据到存储设备。Yii通过MVC架构,非常轻松的完成以上所述的工作流。
以下是用Yii处理form时的经典步骤:
1、为要搜集的数据创建一个模型。
2、创建一个控制器的动作,用来相应表单提交信息。
3、在视图文件中,创建一个表单,用来关联控制器的动作。
接下来,我们详细的描述以上的每个步骤。
1、创建模型
在写表单的HTML代码前,我们先要弄明白的一点:我们希望终端用户提供什么样的数据,以及这些数据必须符合什么规则。模型类就可以用来记录这些信息的,他就是用来保存用户输入信息,并且验证信息的。
基于我们想让用户以何种方式输入,我们可以创建两种类型的模型。如果用户数据采集使用之后,是要丢弃的,那么我们就可以创建一个form model;如果用户数据采集之后是要保存到数据库的,我们就创建一个active record。这两种模型都是继承于相同的基类CModel的,这个基类定义了一些表单常用的接口。
1.1 定义模型类
下面我们创建一个LoginForm的模型类来采集用户在登录时的信息。因为用户登录只是验证用户信息,不需要保存,所以采用form model。(本人认为,实际应用中,用户登录信息除了校验用户的信息,一般都还是需要记录一些信息的,具体依不同项目而定)
class LoginForm extends CFormModel
{
public $username;
public $password;
public $rememberMe=false;
}
以上的代码,声明了LoginForm的3个属性:$username, $password and $rememberMe. 具体干嘛用的不需要翻译了,一目了然。这些属性就是用来保存用户输入的数据或者是数据库的数据。
1.2 声明验证规则
一旦用户提交了数据,模型获取到了输入信息,我们要验证这些输入的合法性。这个工作是由一堆的规则来验证输入的信息。我们在方法rules()中来配置这一些验证规则。
class LoginForm extends CFormModel
{
public $username;
public $password;
public $rememberMe=false;
private $ identity;3.2CreatingModel55
public function rules()
{
return array(
array('username,password', 'required'), //表明用户名,密码是必填项
array('rememberMe', 'boolean'),//布尔型
array('password', 'authenticate'),//需到authenticate验证
);
}
public function authenticate($attribute,$params)
{
$this-> identity=new UserIdentity($this->username,$this->password);
if(!$this-> identity->authenticate())
$this->addError('password','Incorrectusernameorpassword.');
}
}
所有的规则,都必须符合以下的格式:
array('AttributeList', 'Validator', 'on'=>'ScenarioList',...additional options)
AttributeList:需要用这条规则来校验的属性列表,如果是多个属性,中间用, 隔开。
Validator:验证器。也就是指定的这些属性列表,需要用哪个校验器来检验。
on:可选项,用来指明在什么场景需要启用该规则。
有三种方式来选择规则中的验证器。首先,验证器可以是自身模型类的一个方法,如上例中的authenticate。验证器必须符合以下格式:public function ValidatorName($attribute,$params) {...}。第二,这个验证器可以是某个验证器类的类名称。当规则启用的时候,这个验证器类就会创建一个实例并运行起来,执行他的验证功能。规则中的addtional options就是用来初始化这个实例属性的。第三,这个验证器也可以是某个验证器的别名。上例的规则中,'required'就是CRequiredValidator的别名,用来校验所有的属性值不为空。以下列出一份完整的别名名单,能背下来最后,目前我是还没背下来:
boolean, captcha, email, date, default, exist, file, filter, in, length, match, numerical, required, type, unique, url
样例示范:
// 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'),
1.3 安全的给属性赋值
在模型实例化以后,我们通常都需要由终端用户来填充数据。可以简单的由以下代码实现集体赋值(massive assignment):
$model=new LoginForm;
if(isset($ POST['LoginForm']))
$model->attributes=$ POST['LoginForm'];
上例中的最后一行就是集体赋值,把从$_POST['LoginFom']的数据赋值到相对应的模型属性中。作用等同于:
foreach($ POST['LoginForm'] as $name=>$value)
{
if($name is a safe attribute)
$model->$name=$value;
}
他的关键之处在于指定哪些属性是安全的。假设我们把某个表的主键设置为安全类型,那么当黑客有机会修改该记录的主键值,并篡改数据,这些都是不合法的。
声明安全的属性
一个属性,如果在给定的场景下,符合验证规则,那么我们就认为这个属性是安全的。例如
array('username, password', 'required', 'on'=>'login, register'),
array('email', 'required', 'on'=>'register'),
在login, register的时候,用户名,密码是必填项,在register的时候,用户名,密码,email都是必填项。
所以,在用户登录的时候,只有用户名,密码这两个属性会被集体赋值(massive assignment), 因为在登录的时候,规则只有这两个属性;同样的,在注册新用户的时候,以上的三个属性都会被集体赋值。
// in login scenario
$model=new User('login');
if(isset($ POST['User']))
$model->attributes=$ POST['User'];
// in register scenario
$model=new User('register');
if(isset($ POST['User']))
$model->attributes=$ POST['User'];
好了,现在的问题,我们为什么要用这样的策略来决定一个属性是否是安全的呢?想想看,如果一个属性,已经被一条或者是N条的验证规则验证过了,那么我们还有什么好担心的呢?
必须谨记的一点,验证规则主要是用来验证用户的提交数据,而不是代码所生成的数据(例如时间戳,自动生成的主键ID)。所以,千万记住,不要为那些不是从终端用户提交的数据进行规则校验。
有时候,我们想把某些属性设置为安全属性,即使我们并没有规则来校验他。例如一篇文章的内容,可以允许任何的内容输入。这时候我们就可以使用安全规则,来强制说明这个属性是安全的:
array('content', 'safe')
有强制说明安全,那就会有强制说明不安全的了:
array('permission', 'unsafe')
这个被注明为不安全的属性有什么用呢?他很有用,是之前所提到的安全属性的一个例外。
对于这些不安全的属性,我们必须亲自一一为他们赋值,例如:
$model->permission='admin';
$model->id=1;
1.4 触发式校验
一个模型在用户提交数据之后,我们可以调用CModel::validate()的方法来触发数据校验的线程。这个方法返回一个校验的结果,通过或者不通过。对于CActiveRecord的模型来说,在我们调用CActiveRecord::save()的时候,也会自动触发该方法。
我们可以设置一个场景的属性,以及一些验证规则来校验。
校验器是在一个基础的场景使用的,这个场景的属性确定了场景是用哪个模型,以及那些校验规则会被启用。例如,在用户登录(Login)的时候,我们只想校验用户名,密码;在注册新用户的场景时,我们需要验证更多的信息,例如emai,地址等。
// creates a User model in register scenario. It is equivalent to:
// $model=new User;
// $model->scenario='register';
$model=new User('register');
// populates the input values into the model
$model->attributes=$ POST['User'];
// performs the validation
if($model->validate()) // if the inputs are valid
...
else
...
在该场景中,规则是否适用取决于规则中的"on”选项。如果规则的on选项没有被设置,则意味着该规则适用于所有场景。例如:
public function rules()
{
return array(
array('username, password', 'required'),
array('password repeat', 'required', 'on'=>'register'),
array('password', 'compare', 'on'=>'register'),
);
}
第一条规则适用于任何场景,而第二条规则只有在regster的场景时适用。
1.5 捕获校验器错误
在验证完成之后,任何错误都可能存在于模型的对象中。怎么才能获取这些错误信息呢?我们可以通过调用CModel::getErrors() 以及CModel::getError()。这两个的区别在于第一个方法会返回校验后所有的错误信息,而后者只返回第一个错误。
1.6 属性标签
在设计的时候,我们通常需要为要输入的属性设置一个标签,用来告诉终端用户,输入的是什么项目。当然,最原始的方法,我们可以自己在视图里用代码编写一个标签,但是,有更好的方法了。最好是在当前的模型中,提供一个标签,这样既方便又容易维护。
默认情况下,CModel只是简单的用该属性的名称来作为标签。这可以通过重载attributeLabels()这个方法来设置。在后续的章节中,我们会看到在模型中设置标签,会使我们的开发更快速,也更健壮。