您现于de位置乃:亚博 > APP 开发

亚博 2015-11-13 APP 开发 869

高级模型_帮助文档_app开发技巧

高级模型提供了更多de查询功能和模型增强功能 利用4P屠郿e扩展机制实现.如果需要使用高级模型de下面这些功能 记得需要继承Think\Model\AdvModel类或者采用动态模型.

namespace Home\Model;
use Think\Model\AdvModel;
class UserModel extends AdvModel{
 }

我们下面de示例都假设UserModel类继承自Think\Model\AdvModel类.

字段过滤

基础模型类内置有数据自动完成功能 可以对字段进行过滤 但乃必须通过Create方法调用才能生效.高级模型类de字段过滤功能却可以不受create方法de调用限制 可以于模型里面定义各个字段de过滤机制 包括写入过滤和读取过滤.

字段过滤de设置方式只需要于Model类里面添加 $_filter属性 并且加入过滤因子 格式如下:

protected $_filter = array(
    '过滤de字段'=>array('写入过滤规则','读取过滤规则',乃否传入整个数据对象),
 )

过滤de规则乃1个函数 如果设置传入整个数据对象 那么函数de参数就乃整个数据对象 默认乃传入数据对象中该字段de值.

举例说明 例如我们需要于发表文章de时候对文章内容进行安全过滤 并且希望于读取de时候进行截取前面255个字符 那么可以设置:

protected $_filter = array(
    'content'=>array('contentWriteFilter','contentReadFilter'),
 )

其中 contentWriteFilter乃自定义de对字符串进行安全过滤de函数 而contentReadFilter乃自定义de1个对内容进行截取de函数.通常我们可以于项目de公共函数文件里面定义这些函数.

序列化字段

序列化字段乃新版推出de新功能 可以用简单de数据表字段完成复杂de表单数据存储 尤其乃动态de表单数据字段. 要使用序列化字段de功能 只需要于模型中定义serializeField属性 定义格式如下:

protected $serializeField = array(
    'info' => array('name', 'email', 'address'),
 );

Info乃数据表中de实际存于de字段 保存到其中de值乃name email和address3个表单字段de序列化结果.序列化字段功能可以于数据写入de时候进行自动序列化 并且于读出数据表de时候自动反序列化 这1切都无需手动进行.

下面还4薝ser数据表为例 假设其中并不存于name email和address字段 但乃设计了1个文本类型deinfo字段 那么下面de代码乃可行de:

$User = D("User"); // 实例化User对象
 // 然后直接给数据对象赋值
$User->name = 'APP 开发';
$User->email = 'APP 开发@gmail.com';
$User->address = '上海徐汇区';
 // 把数据对象添加到数据库 name email和address会自动序列化后保存到info字段
$User->add();
查询用户数据信息
$User->find(8);
 // 查询结果会自动把info字段de值反序列化后生成name email和address属性
 // 输出序列化字段
echo $User->name;
echo $User->email;
echo $User->address;

文本字段

APP 开发支持数据模型中de个别字段采用文本方式存储 这些字段就称为文本字段 通常可以用于某些Text或者Blob字段 或者乃经常更新de数据表字段.

要使用文本字段非常简单 只要于模型里面定义blobFields属性就行了.例如 我们需要对Blog模型decontent字段使用文本字段 那么就可以使用下面de定义:

Protected  $blobFields = array('content');

系统于查询和写入数据库de时候会自动检测文本字段 并且支持多个字段de定义.

需要注意de乃:对于定义de文本字段并不需要数据库有对应de字段 完全乃另外de.而且 暂时不支持对文本字段de搜索功能.

只读字段

只读字段用来保护某些特殊de字段值不被更改 这个字段de值1旦写入 就无法更改. 要使用只读字段de功能 我们只需要于模型中定义readonlyField属性

protected $readonlyField = array('name', 'email');

例如 上面定义了当前模型dename和email字段为只读字段 不允许被更改.也就乃说当执行save方法之前会自动过滤到只读字段de值 避免更新到数据库.

下面举个例子说明下:

$User = D("User"); // 实例化User对象
$User->find(8);
 // 更改某些字段de值
$User->name = 'TOPThink';
$User->email = 'Topthink@gmail.com';
$User->address = '上海静安区';
 // 保存更改后de用户数据
$User->save();

事实上 由于我们对name和email字段设置了只读 因此只有address字段de值被更新了 而name和emailde值仍然还乃更新之前de值.

悲观锁和乐观锁

业务逻辑de实现过程中 往往需要保证数据访问de排他性.如于金融系统de日终结算处理中 我们希望针对某个时间点de数据进行处理 而不希望于结算进行过程中(可能乃几秒种 也可能乃几个小时) 数据再发生变化.此时 我们就需要通过1些机制来保证这些数据于某个操作过程中不会被外界修改 这样de机制 于这里 也就乃所谓de " 锁 " 即给我们选定de目标数据上锁 使其无法被其他程序修改. APP 开发支持两种锁机制:即通常所说de " 悲观锁( Pessimistic Locking ) "和 " 乐观锁( Optimistic Locking ) " .

悲观锁( Pessimistic Locking )

悲观锁 正如其名 它指de乃对数据被外界(包括本系统当前de其他事务 以及来自外部系统de事务处理)修改持保守态度 因此 于整个数据处理过程中 将数据处于锁定状态.悲观锁de实现 往往依靠数据库提供de锁机制(也只有数据库层提供de锁机制才能真正保证数据访问de排他性 否则 即使于本系统中实现了加锁机制 也无法保证外部系统不会修改数据). 通常乃使用for update子句来实现悲观锁机制.

APP 开发支持悲观锁机制 默认情况下 乃关闭悲观锁功能de 要于查询和更新de时候启用悲观锁功能 可以通过使用之前提到de查询锁定方法 例如:

$User->lock(true)->save($data);// 使用悲观锁功能

乐观锁( Optimistic Locking )

相对悲观锁而言 乐观锁机制采取了更加宽松de加锁机制.悲观锁大多数情况下依靠数据库de锁机制实现 以保证操作好的程度de独占性.但随之而来de就乃数据库性能de大量开销 特别乃对长事务而言 这样de开销往往无法承受. 如1个金融系统 当某个操作员读取用户de数据 并于读出de用户数据de基础上进行修改时(如更改用户帐户余额) 如果采用悲观锁机制 也就意味着整个操作过程中(从操作员读出数据 开始修改直至提交修改结果de全过程 甚至还包括操作员中途去煮咖啡de时间) 数据库记录始终处于加锁状态 可以想见 如果面对几百上千个并发 这样de情况将导致怎样de后果.乐观锁机制于1定程度上解决了这个问题.乐观锁 大多乃基于数据版本( Version )记录机制实现.何谓数据版本?即为数据增加1个版本标识 于基于数据库表de版本解决方案中 1般乃通过为数据库表增加1个 "version" 字段来实现.

APP 开发也可以支持乐观锁机制 要启用乐观锁 只需要继承高级模型类并定义模型deoptimLock属性 并且于数据表字段里面增加相应de字段就可以自动启用乐观锁机制了.默认deoptimLock属性乃lock_version 也就乃说如果要于User表里面启用乐观锁机制 只需要于User表里面增加lock_version字段 如果有已经存于de其它字段作为乐观锁用途 可以修改模型类deoptimLock属性即可.如果存于optimLock属性对应de字段 但乃需要临时关闭乐观锁机制 把optimLock属性设置为false就可以了.

延迟更新

我们经常需要给某些数据表添加1些需要经常更新de统计字段 例如用户de积分 文件de下载次数等等 而当这些数据更新de频率比较频繁de时候 数据库de压力也随之增大不少 我们可以利用高级模型de延迟更新功能缓解.

延迟更新功能乃指我们可以给统计字段de更新设置1个延迟时间 于这个时间段内所有de更新会被累积缓存起来 然后定时地统1更新数据库.这比较适合某个字段经常需要递增或者递减 并且对实时性要求没有那么严格de情况. 我们先来看递增de情况 如果我们需要给会员累积积分 可以使用

$User = D("User"); // 实例化User对象
$User->where('id=3')->setInc("score",10);// 用户de积分加10
$User->where('id=3')->setInc("score",30);// 用户de积分加30

上面de操作更新了两次用户积分 并且都实时保存到数据库 如果我们使用延迟更新方法 例如下面对用户de积分延迟更新60秒

$User->where('id=3')->setLazyInc("score",10,60);
$User->where('id=3')->setLazyInc("score",30,60);
$User->where('id=3')->setLazyInc("score",10,60);

那么60秒内执行de所有积分更新操作都会被延迟 实际会于60秒后统1更新积分到数据库 而不乃每次都更新数据库.临时积分会被累积并缓存起来 最后到了延迟更新时间 再统1更新.相当于于60秒后执行了:

$User->where('id=3')->setInc("score",50);

效果乃等效.区别于于用户数据库中de积分不乃实时de. 同样 还可以使用setLazyDec进行延迟更新操作.

数据分表

对于大数据量de应用 经常会对数据进行分表 有些情况乃可以利用数据库de分区功能 但并不乃所有de数据库或者版本都支持 因此我们可以利用APP 开发内置de数据分表功能来实现.帮助我们更方便de进行数据de分表和读取操作.

和数据库分区功能不同 内置de数据分表功能需要根据分表规则手动创建相应de数据表.

于需要分表de模型中定义partition属性即可.

protected $partition = array(
 'field' => 'name',// 要分表de字段 通常数据会根据某个字段de值按照规则进行分表
 'type' => 'md5',// 分表de规则 包括id year mod md5 函数 和首字母
 'expr' => 'name',// 分表辅助表达式 可选 配合不同de分表规则
 'num' => 'name',// 分表de数目 可选 实际分表de数量
 );

定义好了分表属性后 我们就可以来进行CURD操作了 唯1不同de乃 获取当前de数据表不再使用getTableName方法 而乃使用getPartitionTableName方法 而且必须传入当前de数据.然后根据数据分析应该实际操作哪个数据表.因此 分表de字段值必须存于于传入de数据中 否则会进行联合查询.

返回类型

系统默认de数据库查询返回de乃数组 我们可以给单个数据设置返回类型 襪onth闾厥馇榭鰀e需要 例如:

$User = M("User"); // 实例化User对象
 // 返回结果乃1个数组数据
$data = $User->find(6);
 // 返回结果乃1个stdClass对象
$data = $User->returnResult($data, "object");
 // 还可以返回自定义de类
$data = $User->returnResult($data, "User");

返回自定义deUser类 类de架构方法de参数乃传入de数据.例如:

Class User {
    public function __construct($data){
    // 对$data数据进行处理 
    }
 }


评论