文档索引 » QeePHP 快速入门 » 实现用户功能 » 用户注册(完善模型)

用户注册(完善模型)

QeePHP 是一个遵循“领域驱动开发”思想的框架。而领域驱动中,最重要的就是模型。我们的大多数功能都是和模型有关的,所以在实现功能之前,首先要完善模型的定义。

先前创建的 User 模型只是半成品,现在我们需要对其进行完善。一个模型由几个方面组成:

  • 属性:定义模型有哪些属性、属性的类型以及属性的行为和验证规则等
  • 行为:添加实现业务逻辑需要的方法,让模型逐步逼近实际的需求
  • 数据:由属性和存储模型属性的数据表组成,但属性不一定要和数据表一一对应
  • 关系:模型需要互相配合来实现业务需求,因此关系也是模型的一部分

完善模型的属性

打开模型的定义文件,在 __define() 方法中可以找到模型的详细定义。这些定义分为几个部分:

  • 指定存储模型使用的数据表
  • 属性和关联的定义,例如只读属性、虚拟属性、关联等
  • 对属性的验证规则
  • 安全性方面的定义
  • 行为插件的定义

在 QeePHP 的模型中,属性本身具有多种特性:

  • 是否是只读属性
  • 是否是虚拟属性
  • 是否有 getter 或 setter 方法
  • 类型
  • 验证规则

对于 todo 这样的简单应用,我们用不到这么多高级特性,所以上述内容了解一下就行了。

todo 应用中,user 模型只有两个主要属性:用户名和密码。根据需求,这两个属性有下列要求:

  • 用户名应该是唯一的
  • 用户一旦成功创建,就不能更改用户名,因此用户名必须是只读属性
  • 用户名不能少于 5 个字符,不能多于 15 个字符,并且只能使用字母和数字组成
  • 密码不能少于 6 个字符,不能多于 20 个字符
  • 密码应该加密存储

现在我们一步步来实现这些要求。

在 __define() 方法中的适当位置加入下列代码:

'props' => array(
    .....
    // 指定 username 属性为只读
    'username' => array('readonly' => true),
    .....
),
 
/**
 * 指定更新数据库中的对象时,哪些属性的值不允许由外部提供
 */
'update_reject' => 'username',

继续:

'validations' => array
(
    // 指定 username 属性的验证规则
    'username' => array
    (
        array('not_empty', '用户名不能为空'),
        array('min_length', 5, '用户名不能少于 5 个字符'),
        array('max_length', 15, '用户名不能超过 15 个字符'),
        array('is_alnum', '用户名只能由字母和数字组成'),
    ),
 
    // 指定 password 属性的验证规则
    'password' => array
    (
        array('not_empty', '密码不能为空'),
        array('min_length', 6, '密码不能少于 6 个字符'),
        array('max_length', 20, '密码不能超过 20 个字符'),
    ),
),

经过简单的设置,大部分要求都可以由 QeePHP 来处理了,剩下的密码加密存储则可以借助现成的 acluser 行为插件来实现。

行为插件

虽然我们可以通过自行编写代码来实现用户密码的加密存储,但 QeePHP 已经提供了现成的插件来帮助我们完成类似工作,并且提供了更丰富的特性。

acluser 行为插件应用到一个模型后,可以实现下列特征:

  • 密码属性的自动加密
  • 验证用户名和密码的正确性
  • 验证用户名的唯一性
  • 查询用户的基本属性和角色信息

要使用 acluser 插件,只需要修改 __define() 方法:

// 指定该 ActiveRecord 要使用的行为插件
'behaviors' => 'acluser',
 
// 指定行为插件的配置
'behaviors_settings' => array
(
    # '插件名' => array('选项' => 设置),
    'acluser' => array(
        'acl_data_props' => 'username',
    ),
),

acluser 插件的详细文档请参考: http://qeephp.com/docs/qeephp-api/class-model-behavior-acluser

最后,完整的 __define() 方法应该有如下内容:

static function __define()
{
    return array
    (
        // 指定该 ActiveRecord 要使用的行为插件
        'behaviors' => 'acluser',
 
        // 指定行为插件的配置
        'behaviors_settings' => array
        (
            # '插件名' => array('选项' => 设置),
            'acluser' => array(
                'acl_data_props' => 'username',
            ),
        ),
 
        // 用什么数据表保存对象
        'table_name' => 'users',
 
        // 指定数据表记录字段与对象属性之间的映射关系
        // 没有在此处指定的属性,QeePHP 会自动设置将属性映射为对象的可读写属性
        'props' => array
        (
            // 主键应该是只读,确保领域对象的“不变量”
            'user_id' => array('readonly' => true),
 
            /**
             *  可以在此添加其他属性的设置
             */
            # 'other_prop' => array('readonly' => true),
            'username' => array('readonly' => true),
 
            /**
             * 添加对象间的关联
             */
            # 'other' => array('has_one' => 'Class'),
 
            'tasks' => array(QDB::HAS_MANY => 'Task', 'target_key' => 'user_id'),
        ),
 
        /**
         * 允许使用 mass-assignment 方式赋值的属性
         *
         * 如果指定了 attr_accessible,则忽略 attr_protected 的设置。
         */
        'attr_accessible' => '',
 
        /**
         * 拒绝使用 mass-assignment 方式赋值的属性
         */
        'attr_protected' => 'user_id',
 
        /**
         * 指定在数据库中创建对象时,哪些属性的值不允许由外部提供
         *
         * 这里指定的属性会在创建记录时被过滤掉,从而让数据库自行填充值。
         */
        'create_reject' => '',
 
        /**
         * 指定更新数据库中的对象时,哪些属性的值不允许由外部提供
         */
        'update_reject' => 'username',
 
        /**
         * 指定在数据库中创建对象时,哪些属性的值由下面指定的内容进行覆盖
         *
         * 如果填充值为 self::AUTOFILL_TIMESTAMP 或 self::AUTOFILL_DATETIME,
         * 则会根据属性的类型来自动填充当前时间(整数或字符串)。
         *
         * 如果填充值为一个数组,则假定为 callback 方法。
         */
        'create_autofill' => array
        (
            # 属性名 => 填充值
            # 'is_locked' => 0,
        ),
 
        /**
         * 指定更新数据库中的对象时,哪些属性的值由下面指定的内容进行覆盖
         *
         * 填充值的指定规则同 create_autofill
         */
        'update_autofill' => array
        (
        ),
 
        /**
         * 在保存对象时,会按照下面指定的验证规则进行验证。验证失败会抛出异常。
         *
         * 除了在保存时自动验证,还可以通过对象的 ::meta()->validate() 方法对数组数据进行验证。
         *
         * 如果需要添加一个自定义验证,应该写成
         *
         * 'title' => array(
         *        array(array(__CLASS__, 'checkTitle'), '标题不能为空'),
         * )
         *
         * 然后在该类中添加 checkTitle() 方法。函数原型如下:
         *
         * static function checkTitle($title)
         *
         * 该方法返回 true 表示通过验证。
         */
        'validations' => array
        (
            'username' => array
            (
                array('not_empty', '用户名不能为空'),
                array('min_length', 5, '用户名不能少于 5 个字符'),
                array('max_length', 15, '用户名不能超过 15 个字符'),
                array('is_alnum', '用户名只能由字母和数字组成'),
            ),
 
            'password' => array
            (
                array('not_empty', '密码不能为空'),
                array('min_length', 6, '密码不能少于 6 个字符'),
                array('max_length', 20, '密码不能超过 20 个字符'),
            ),
        ),
    );
}
qeephp.com