The DoYouHaoBaby PHP Framework中文开发文档手册
Copyright(c) DoYouHaoBaby.NET All Rights Reserved.
Copyright(c) DoYouHaoBaby.NET All Rights Reserved.
[ 用户注册(完善模型) ]
DoYouHaoBaby 继承了QeePHP 关于 领域驱动设计 的功能,所以我们也是一个遵循“领域驱动开发”思想的框架。
而领域驱动中,最重要的就是模型。我们的大多数功能都是和模型有关的,所以在实现功能之前,首先要完善模型的定义。
先前创建的 User 模型只是半成品,现在我们需要对其进行完善。一个模型由几个方面组成:
| 属性 | 定义模型有哪些属性、属性的类型以及属性的行为和验证规则等 |
| 行为 | 添加实现业务逻辑需要的方法,让模型逐步逼近实际的需求 |
| 数据 | 由属性和存储模型属性的数据表组成,但属性不一定要和数据表一一对应 |
| 关系 | 模型需要互相配合来实现业务需求,因此关系也是模型的一部分 |
完善模型的属性
打开模型的定义文件,在 init__() 方法中可以找到模型的详细定义。这些定义分为几个部分:
指定存储模型使用的数据表
- 属性和关联的定义,例如只读属性、虚拟属性、关联等
- 对属性的验证规则
- 安全性方面的定义
- 行为插件的定义
在 DoYouHaoBaby 的模型中,属性本身具有多种特性:
- 是否是只读属性
- 是否是虚拟属性
- 是否有 getter 或 setter 方法
- 类型
验证规则
对于 wedoit 这样的简单应用,我们用不到这么多高级特性,所以上述内容了解一下就行了。
wedoit 应用中,user 模型只有两个主要属性:用户名和密码。根据需求,这两个属性有下列要求:
- 用户名应该是唯一的
- 用户一旦成功创建,就不能更改用户名,因此用户名必须是只读属性
- 用户名不能少于 5 个字符,不能多于 15 个字符,并且只能使用字母和数字组成
- 密码不能少于 6 个字符,不能多于 20 个字符
- 密码应该加密存储
现在我们一步步来实现这些要求。
在 init__() 方法中的适当位置加入下列代码:
- 'props' => array(
- // 主键应该是只读,确保领域对象的“不变量”
- 'user_id' => array( 'readonly' => true ),
- // 可以在此添加其他属性的设置
- 'user_name' => array( 'readonly' => true ),
- ),
- /**
- * 指定更新数据库中的对象时,哪些属性的值不允许由外部提供
- */
- 'update_reject' => 'user_name',
继续:
- 'check' => array(
- 'user_name' => array(
- array('require', '用户名不能为空'),
- array('min_length', 5, '用户名不能少于 5 个字符'),
- array('max_length', 15, '用户名不能超过 15 个字符'),
- array('alnum', '用户名只能由字母和数字组成'),
- ),
- 'user_password' => array(
- array('require', '密码不能为空'),
- array('min_length', 6, '密码不能少于 6 个字符'),
- array('max_length', 20, '密码不能超过 20 个字符'),
- ),
- )
经过简单的设置,大部分要求都可以由 DoYouHaoBaby 来处理了,剩下的密码加密存储则可以借助现成的 rbac 行为插件来实现。
行为插件
虽然我们可以通过自行编写代码来实现用户密码的加密存储,但 DoYouHaoBaby 已经提供了现成的插件来帮助我们完成类似工作,并且提供了更丰富的特性。
rbac 行为插件应用到一个模型后,可以实现下列特征:
- 密码属性的自动加密
- 验证用户名和密码的正确性
- 查询用户的基本属性和角色信息
要使用 rbac 插件,只需要修改 init__() 方法:
- /**
- * 指定该 ActiveRecord 要使用的行为插件
- *
- * @return string
- */
- 'behaviors' => 'rbac',
- /**
- * 指定行为插件的配置
- *
- * @return array
- */
- 'behaviors_settings' => array(
- // '插件名' => array( '选项' => 设置 ),
- 'rbac' => array(
- 'password_prop' => 'user_password',
- 'rbac_data_props' => 'user_name,user_id,user_password',
- 'encode_type'=>'md5',
- 'auth_thin'=>true,
- 'register_save_auto'=>false,
- 'update_login_auto'=>false,
- ),
- ),
最后,完整的 init__() 方法应该有如下内容:
- /**
- * 返回对象的定义
- *
- * @access public
- * @static
- * @return array
- */
- static public function init__(){
- return array(
- /**
- * 指定该 ActiveRecord 要使用的行为插件
- *
- * @return string
- */
- 'behaviors' => 'rbac',
- /**
- * 指定行为插件的配置
- *
- * @return array
- */
- 'behaviors_settings' => array(
- // '插件名' => array( '选项' => 设置 ),
- 'rbac' => array(
- 'password_prop' => 'user_password',
- 'rbac_data_props' => 'user_name,user_id,user_password',
- 'encode_type'=>'md5',
- 'auth_thin'=>true,
- 'register_save_auto'=>false,
- 'update_login_auto'=>false,
- ),
- ),
- /**
- * 用什么数据表保存对象
- *
- * < 数据库表不带前缀 >
- *
- * @return string
- */
- 'table_name' => 'user',
- /**
- * 指定数据表记录字段与对象属性之间的映射关系
- *
- * < 指示模型中各个属性、关联的设置
- * 没有在这里指定设置的数据表字段自动成为模型的普通属性。
- * 例如数据表中有 user_id、user_title 两个字段,而 props 中
- * 没有指定 user_title 属性的设置,则 user_title 字段成为该模型的 user_title 属性,并且可以读写。>
- *
- * @return array
- */
- 'props' => array(
- // 主键应该是只读,确保领域对象的“不变量”
- 'user_id' => array( 'readonly' => true ),
- // 可以在此添加其他属性的设置
- 'user_name' => array( 'readonly' => true ),
- ),
- /**
- * 拒绝使用 mass-assignment 方式赋值的属性
- *
- * < 功能同上,如果上面是白名单的话,那么下面这个就是黑名单。
- * 这里设置了属性,那么设置后的属性不能通过构造函数来赋值。 >
- *
- * @return mixed
- */
- 'attr_protected' => 'user_id',
- /**
- * 指定更新数据库中的对象时,哪些属性的值不允许由外部提供
- *
- * < 同上,只是一个为创建数据对象时,一个为更新数据对象时 >
- *
- * @return mixed
- */
- 'update_reject' => 'user_name',
- /**
- * 在保存对象时,会按照下面指定的验证规则进行验证。验证失败会发送错误,我们可以通过系统提供的方法拦截。
- *
- * < 除了在保存时自动验证,还可以通过对象的 模型::M()->check( ) 方法对数组数据进行验证。
- * 系统提供了50多个验证规则,所有验证规则可以在 DoYouHaoBaby 完全开发手册中 ~handbook\package\check\check.html中全部罗列出来。 >
- *
- * < 如果需要添加一个自定义验证,应该写成 >
- * < 'title' => array(
- *
- * array( array(__CLASS__, 'checkTitle'), '标题不能为空'),
- * ) >
- *
- * < 同时上面的验证规则,可以用基于ThinkPHP的验证规则做到,下面我们将会有实例来做。>
- *
- * @return array
- */
- 'check' => array(
- 'user_name' => array(
- array('require', '用户名不能为空'),
- array('min_length', 5, '用户名不能少于 5 个字符'),
- array('max_length', 15, '用户名不能超过 15 个字符'),
- array('alnum', '用户名只能由字母和数字组成'),
- ),
- 'user_password' => array(
- array('require', '密码不能为空'),
- array('min_length', 6, '密码不能少于 6 个字符'),
- array('max_length', 20, '密码不能超过 20 个字符'),
- ),
- ),
- );
- }
