lz19881123 2011-08-10 14:27:27 5065次浏览 1条回复 5 0 0

Ⅱ、使用表单 在 Yii 中处理表单时,通常需要以下步骤:

  1. 创建用于表现所要收集数据字段的模型类。
  2. 创建一个控制器动作,响应表单提交。
  3. 在视图脚本中创建与控制器动作相关的表单。 一、创建模型 在编写表单所需的 HTML 代码之前,我们应该先确定来自最终用户输入的数据的类型,以及这些数据应符合什么样的规则。模型类可用于记录这些信息。正如模型章节所定义的,模型是保存用户输入和验证这些输入的中心位置。 取决于使用用户所输入数据的方式,我们可以创建两种类型的模型。如果用户输入被收集、使用然后丢弃,我们应该创建一个表单模型; 如果用户的输入被收集后要保存到数据库,我们应使用一个Active Record。两种类型的模型共享同样的基类 CModel ,它定义了表单所需的通用接口。 1、定义模型类 例如创建为一个表单模型:
class LoginForm extends CFormModel
{
    public $username;
    public $password;
    public $rememberMe=false;
}

LoginForm 中定义了三个属性: $username, $password 和 $rememberMe。他们用于保存用户输入的用户名和密码,还有用户是否想记住他的登录的选项。由于 $rememberMe 有一个默认的值 false,相应的选项在初始化显示在登录表单中时将是未勾选状态。 我们将这些成员变量称为特性(attributes)而不是属性(properties),以区别于普通的属性(properties)。特性(attribute)是一个主要用于存储来自用户输入或数据库数据的属性(propertiy)。 2、声明验证规则 一旦用户提交了他的输入,模型被填充,我们就需要在使用前确保用户的输入是有效的。这是通过将用户的输入和一系列规则执行验证实现的。我们在 rules() 方法中指定这些验证规则,此方法应返回一个规则配置数组。

class LoginForm extends CFormModel
{
    public $username;
    public $password;
    public $rememberMe=false;
 
    private $_identity;
 
    public function rules()
    {
        return array(
            array('username, password', 'required'), //username 和 password 为必填项
            array('rememberMe', 'boolean'), //rememberMe 应该是一个布尔值
            array('password', 'authenticate'), //password 应被验证(authenticated)
        );
    }
 
    public function authenticate($attribute,$params)
    {
        $this->_identity=new UserIdentity($this->username,$this->password);
        if(!$this->_identity->authenticate())
            $this->addError('password','错误的用户名或密码。');
    }
}

rules() 返回的每个规则必须是以下格式:

array('AttributeList', 'Validator', 'on'=>'ScenarioList', ...附加选项)

其中: AttributeList(特性列表)是需要通过此规则验证的特性列表字符串,每个特性名字由逗号分隔; Validator(验证器) 指定要执行验证的种类; on 参数是可选的,它指定此规则应被应用到的场景列表; 附加选项 是一个名值对数组,用于初始化相应验证器的属性值。

有三种方式可在验证规则中指定 Validator: 第一, Validator 可以是模型类中一个方法的名字,就像上面示例中的 authenticate 。验证方法必须是下面的结构:

/**
 * @param string 所要验证的特性的名字
 * @param array 验证规则中指定的选项
 */

public function 验证器名称($attribute,$params) { ... } 第二,Validator可以是一个验证器类的名字,当此规则被应用时,一个验证器类的实例将被创建以执行实际验证。规则中的附加选项用于初始化实例的属性值。验证器类必须继承自 CValidator。 第三,Validator 可以是一个预定义的验证器类的别名。在上面的例子中,required 名字是 CRequiredValidator 的别名,它用于确保所验证的特性值不为空。下面是预定义的验证器别名的完整列表: boolean: CBooleanValidator 的别名,确保特性有一个 CBooleanValidator::trueValue 或 CBooleanValidator::falseValue 值。 captcha: CCaptchaValidator 的别名,确保特性值等于 CAPTCHA 中显示的验证码。 compare: CCompareValidator 的别名,确保特性等于另一个特性或常量。 email: CEmailValidator 的别名,确保特性是一个有效的Email地址。 default: CDefaultValueValidator 的别名,指定特性的默认值。 exist: CExistValidator 的别名,确保特性值可以在指定表的列中可以找到。 file: CFileValidator 的别名,确保特性含有一个上传文件的名字。 filter: CFilterValidator 的别名,通过一个过滤器改变此特性。 in: CRangeValidator 的别名,确保数据在一个预先指定的值的范围之内。 length: CStringValidator 的别名,确保数据的长度在一个指定的范围之内。 match: CRegularExpressionValidator 的别名,确保数据可以匹配一个正则表达式。 numerical: CNumberValidator 的别名,确保数据是一个有效的数字。 required: CRequiredValidator 的别名,确保特性不为空。 type: CTypeValidator 的别名,确保特性是指定的数据类型。 unique: CUniqueValidator 的别名,确保数据在数据表的列中是唯一的。 url: CUrlValidator 的别名,确保数据是一个有效的 URL。

下面我们列出了几个只用这些预定义验证器的示例:

// 用户名为必填项
array('username', 'required'),
// 用户名必须在 3 到 12 个字符之间
array('username', 'length', 'min'=>3, 'max'=>12),
// 在注册场景中,密码password必须和password2一致。
array('password', 'compare', 'compareAttribute'=>'password2', 'on'=>'register'),
// 在登录场景中,密码必须接受验证。
array('password', 'authenticate', 'on'=>'login'),

3、安全的特性赋值 在一个类的实例被创建后,我们通常需要用最终用户提交的数据填充它的特性。这可以通过如下块赋值(massive assignment)方式轻松实现:

$model=new LoginForm;
if(isset($_POST['LoginForm']))
    $model->attributes=$_POST['LoginForm'];

最后的表达式被称作 块赋值(massive assignment) ,它将 $_POST['LoginForm'] 中的每一项复制到相应的模型特性中。这相当于如下赋值方法:

foreach($_POST['LoginForm'] as $name=>$value)
{
    if($name 是一个安全的特性)
        $model->$name=$value;
}

检测特性的安全非常重要,例如,如果我们以为一个表的主键是安全的而暴露了它,那么攻击者可能就获得了一个修改记录的主键的机会,从而篡改未授权给他的内容。 特性如果出现在相应场景的一个验证规则中,即被认为是安全的。例如:

array('username, password', 'required', 'on'=>'login, register'),
array('email', 'required', 'on'=>'register'),

如上所示, username 和 password 特性在 login 场景中是必填项。而 username, password 和 email 特性在 register 场景中是必填项。于是,如果我们在 login 场景中执行块赋值,就只有 username 和 password 会被块赋值。因为只有它们出现在 login 的验证规则中。另一方面,如果场景是 register ,这三个特性就都可以被块赋值。

// 在登录场景中
$model=new User('login');
if(isset($_POST['User']))
  $model->attributes=$_POST['User'];
// 在注册场景中
$model=new User('register');
if(isset($_POST['User']))
  $model->attributes=$_POST['User'];

那么为什么我们使用这样一种策略来检测特性是否安全呢?背后的基本原理就是:如果一个特性已经有了一个或多个可检测有效性的验证规则,那我们还担心什么呢? 请记住,验证规则是用于检查用户输入的数据,而不是检查我们在代码中生成的数据(例如时间戳,自动产生的主键)。因此,不要为那些不接受最终用户输入的特性添加验证规则。 有时候,我们想声明一个特性是安全的,即使我们没有为它指定任何规则。例如,一篇文章的内容可以接受用户的任何输入。我们可以使用特殊的 safe 规则实现此目的:

array('content', 'safe')

还有一个用于声明一个属性为不安全的 unsafe 规则:

array('permission', 'unsafe')

unsafe 规则并不常用,它是我们之前定义的安全特性的一个例外。

  • 回复于 2017-02-11 10:39 举报

    返回的规则验证数组,如果是多个特性需要被同一个规则验证,那么这里的多个特性应该被写成一个索引数组的形式。
    题主的第一个规则:

    public function rules()
        {
            return array(
                array('username, password', 'required'), //username 和 password 为必填项
                array('rememberMe', 'boolean'), //rememberMe 应该是一个布尔值
                array('password', 'authenticate'), //password 应被验证(authenticated)
            );
        }
    

    不应该是这种写法吗?

    public function rules()
        {
            return array(
                array(array('username', 'password'), 'required'), //username 和 password 为必填项
                array('rememberMe', 'boolean'), //rememberMe 应该是一个布尔值
                array('password', 'authenticate'), //password 应被验证(authenticated)
            );
        }
    
您需要登录后才可以回复。登录 | 立即注册