The DoYouHaoBaby PHP Framework中文开发文档手册
Copyright(c) DoYouHaoBaby.NET All Rights Reserved.
[ 用户注册(完善模型) ]

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

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

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

完善模型的属性

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

指定存储模型使用的数据表

  1. 属性和关联的定义,例如只读属性、虚拟属性、关联等
  2. 对属性的验证规则
  3. 安全性方面的定义
  4. 行为插件的定义

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

  1. 是否是只读属性
  2. 是否是虚拟属性
  3. 是否有 getter 或 setter 方法
  4. 类型


验证规则

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

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

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


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

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

  1. 'props' => array(   
  2.   
  3.     // 主键应该是只读,确保领域对象的“不变量”   
  4.     'user_id' => array( 'readonly' => true ),   
  5.   
  6.     // 可以在此添加其他属性的设置   
  7.      'user_name' => array( 'readonly' => true ),   
  8. ),   
  9.     
  10. /**  
  11.  * 指定更新数据库中的对象时,哪些属性的值不允许由外部提供  
  12.  */  
  13. 'update_reject' => 'user_name',  

继续:

  1. 'check' => array(   
  2.   
  3.      'user_name' => array(   
  4.   
  5.           array('require', '用户名不能为空'),   
  6.           array('min_length', 5, '用户名不能少于 5 个字符'),   
  7.           array('max_length', 15, '用户名不能超过 15 个字符'),   
  8.           array('alnum', '用户名只能由字母和数字组成'),   
  9.      ),   
  10.     
  11.      'user_password' => array(   
  12.     
  13.          array('require', '密码不能为空'),   
  14.          array('min_length', 6, '密码不能少于 6 个字符'),   
  15.          array('max_length', 20, '密码不能超过 20 个字符'),   
  16.     ),   
  17.   
  18. )  

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

行为插件

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

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

  1. 密码属性的自动加密
  2. 验证用户名和密码的正确性
  3. 查询用户的基本属性和角色信息


要使用 rbac 插件,只需要修改 init__() 方法:

  1.          /**  
  2.  * 指定该 ActiveRecord 要使用的行为插件  
  3.  *  
  4.  * @return string  
  5.  */  
  6.          'behaviors' => 'rbac',   
  7.   
  8.   
  9. /**  
  10.  * 指定行为插件的配置  
  11.  *  
  12.  * @return array  
  13.  */  
  14.          'behaviors_settings' => array(   
  15.   
  16.               // '插件名' => array( '选项' => 设置 ),   
  17.     'rbac' => array(   
  18.   
  19.                  'password_prop' => 'user_password',   
  20.                  'rbac_data_props' => 'user_name,user_id,user_password',   
  21.         'encode_type'=>'md5',   
  22.         'auth_thin'=>true,   
  23.         'register_save_auto'=>false,   
  24.                  'update_login_auto'=>false,   
  25.             ),   
  26.          ),  

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

  1. /**  
  2.     * 返回对象的定义  
  3.     *  
  4.  * @access public  
  5.     * @static  
  6.     * @return array  
  7.     */  
  8.    static public function init__(){   
  9.   
  10.        return array(   
  11.   
  12.   
  13.            /**  
  14.          * 指定该 ActiveRecord 要使用的行为插件  
  15.          *  
  16.          * @return string  
  17.          */  
  18.            'behaviors' => 'rbac',   
  19.   
  20.   
  21.         /**  
  22.          * 指定行为插件的配置  
  23.          *  
  24.          * @return array  
  25.          */  
  26.            'behaviors_settings' => array(   
  27.   
  28.                 // '插件名' => array( '选项' => 设置 ),   
  29.             'rbac' => array(   
  30.   
  31.                    'password_prop' => 'user_password',   
  32.                    'rbac_data_props' => 'user_name,user_id,user_password',   
  33.                 'encode_type'=>'md5',   
  34.                 'auth_thin'=>true,   
  35.                 'register_save_auto'=>false,   
  36.                    'update_login_auto'=>false,   
  37.               ),   
  38.            ),   
  39.   
  40.   
  41.         /**  
  42.          * 用什么数据表保存对象  
  43.          *  
  44.          * < 数据库表不带前缀 >  
  45.          *  
  46.          * @return string  
  47.          */  
  48.            'table_name' => 'user',   
  49.   
  50.         /**  
  51.          * 指定数据表记录字段与对象属性之间的映射关系  
  52.          *  
  53.          * < 指示模型中各个属性、关联的设置  
  54.          *   没有在这里指定设置的数据表字段自动成为模型的普通属性。  
  55.          *   例如数据表中有 user_id、user_title 两个字段,而 props 中  
  56.             *   没有指定 user_title 属性的设置,则 user_title 字段成为该模型的 user_title 属性,并且可以读写。>  
  57.          *  
  58.          * @return array  
  59.          */  
  60.            'props' => array(   
  61.   
  62.                // 主键应该是只读,确保领域对象的“不变量”   
  63.                'user_id' => array( 'readonly' => true ),   
  64.   
  65.             // 可以在此添加其他属性的设置   
  66.                'user_name' => array( 'readonly' => true ),   
  67.            ),   
  68.   
  69.   
  70.            /**  
  71.             * 拒绝使用 mass-assignment 方式赋值的属性  
  72.          *  
  73.          * < 功能同上,如果上面是白名单的话,那么下面这个就是黑名单。  
  74.          *   这里设置了属性,那么设置后的属性不能通过构造函数来赋值。 >  
  75.             *  
  76.          * @return mixed  
  77.             */  
  78.            'attr_protected' => 'user_id',   
  79.   
  80.   
  81.   
  82.         /**  
  83.             * 指定更新数据库中的对象时,哪些属性的值不允许由外部提供  
  84.             *  
  85.          * < 同上,只是一个为创建数据对象时,一个为更新数据对象时 >  
  86.          *  
  87.          * @return mixed  
  88.             */  
  89.            'update_reject' => 'user_name',   
  90.   
  91.   
  92.            /**  
  93.             * 在保存对象时,会按照下面指定的验证规则进行验证。验证失败会发送错误,我们可以通过系统提供的方法拦截。  
  94.             *  
  95.             * < 除了在保存时自动验证,还可以通过对象的 模型::M()->check( ) 方法对数组数据进行验证。  
  96.          *   系统提供了50多个验证规则,所有验证规则可以在 DoYouHaoBaby 完全开发手册中 ~handbook\package\check\check.html中全部罗列出来。 >  
  97.             *  
  98.             * < 如果需要添加一个自定义验证,应该写成 >  
  99.             * < 'title' => array(  
  100.          *  
  101.             *     array( array(__CLASS__, 'checkTitle'), '标题不能为空'),  
  102.             *  ) >  
  103.             *  
  104.             * < 同时上面的验证规则,可以用基于ThinkPHP的验证规则做到,下面我们将会有实例来做。>  
  105.          *  
  106.          * @return array  
  107.             */  
  108.            'check' => array(   
  109.   
  110.                'user_name' => array(   
  111.   
  112.                    array('require', '用户名不能为空'),   
  113.                    array('min_length', 5, '用户名不能少于 5 个字符'),   
  114.                    array('max_length', 15, '用户名不能超过 15 个字符'),   
  115.                    array('alnum', '用户名只能由字母和数字组成'),   
  116.                ),   
  117.   
  118.                'user_password' => array(   
  119.   
  120.                    array('require', '密码不能为空'),   
  121.                    array('min_length', 6, '密码不能少于 6 个字符'),   
  122.                    array('max_length', 20, '密码不能超过 20 个字符'),   
  123.               ),   
  124.   
  125.            ),   
  126.   
  127.        );   
  128.   
  129.    }