thinkphp 模型

本文详细介绍了ThinkPHP框架中的模型操作,包括模型定义、数据表定义、实例化、字段定义、全局配置、连贯操作、数据写入、读取、更新、删除等关键概念。还探讨了如ALIAS、命名范围、CURD操作、查询语言、自动验证和自动完成等功能,以及如何处理关联模型,如HAS_ONE、BELONGS_TO、HAS_MANY和MANY_TO_MANY的关联关系。此外,文章还涉及到了悲观锁、乐观锁、数据分表和Mongo模型的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

模型定义

模型类通常需要继承系统的\Think\Model类或其子类,下面是一个Home\Model\UserModel类的定义:

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

模型类的作用大多数情况是操作数据表的,如果按照系统的规范来命名模型类的话,大多数情况下是可以自动对应数据表。

 

模型类的命名规则是除去表前缀的数据表名称,采用驼峰法命名,并且首字母大写,然后加上模型层的名称(默认定义是Model),例如:

模型名约定对应数据表(假设数据库的前缀定义是 think_)
UserModelthink_user
UserTypeModelthink_user_type

如果你的规则和上面的系统约定不符合,那么需要设置Model类的数据表名称属性,以确保能够找到对应的数据表。

数据表定义

在ThinkPHP的模型里面,有几个关于数据表名称的属性定义:

属性说明
tablePrefix定义模型对应数据表的前缀,如果未定义则获取配置文件中的DB_PREFIX参数
tableName不包含表前缀的数据表名称,一般情况下默认和模型名称相同,只有当你的表名和当前的模型类的名称不同的时候才需要定义。
trueTableName包含前缀的数据表名称,也就是数据库中的实际表名,该名称无需设置,只有当上面的规则都不适用的情况或者特殊情况下才需要设置。
dbName定义模型当前对应的数据库名称,只有当你当前的模型类对应的数据库名称和配置文件不同的时候才需要定义。

举个例子来加深理解,例如,在数据库里面有一个think_categories表,而我们定义的模型类名称是CategoryModel,按照系统的约定,这个模型的名称是Category,对应的数据表名称应该是think_category(全部小写),但是现在的数据表名称是think_categories,因此我们就需要设置tableName属性来改变默认的规则(假设我们已经在配置文件里面定义了DB_PREFIX 为 think_)。

namespace Home\Model;
use Think\Model;
class CategoryModel extends Model {
    protected $tableName = 'categories'; 
}

注意这个属性的定义不需要加表的前缀think_

如果我们需要CategoryModel模型对应操作的数据表是 top_category,那么我们只需要设置数据表前缀即可:

namespace Home\Model;
use Think\Model;
class CategoryModel extends Model {
    protected $tablePrefix = 'top_'; 
}

如果你的数据表直接就是category,而没有前缀,则可以设置tablePrefix为空字符串。

namespace Home\Model;
use Think\Model;
class CategoryModel extends Model {
    protected $tablePrefix = ''; 
}

没有表前缀的情况必须设置,否则会获取当前配置文件中的 DB_PREFIX

而对于另外一种特殊情况,我们需要操作的数据表是top_categories,这个时候我们就需要定义 trueTableName 属性

namespace Home\Model;
use Think\Model;
class CategoryModel extends Model {
    protected $trueTableName = 'top_categories'; 
}

注意trueTableName需要完整的表名定义。

除了数据表的定义外,还可以对数据库进行定义(用于操作当前数据库以外的数据表),例如 top.top_categories

namespace Home\Model;
use Think\Model;
class CategoryModel extends Model {
    protected $trueTableName = 'top_categories'; 
    protected $dbName = 'top';
}

实例化模型

D()方法;

D方法可以自动检测模型类,如果存在自定义的模型类,则实例化自定义模型类,如果不存在,则会实例化系统的\Think\Model基类,同时对于已实例化过的模型,不会重复实例化。

 

M方法实例化模型

例如:

// 使用M方法实例化
$User = M('User');
// 和用法 $User = new \Think\Model('User'); 等效
// 执行其他的数据操作
$User->select();

M方法也可以支持跨库操作,例如:

// 使用M方法实例化 操作db_name数据库的ot_user表
$User = M('db_name.User','ot_');
// 执行其他的数据操作
$User->select();

M方法实例化的时候,默认情况下是直接实例化系统的\Think\Model类,如果我们希望实例化其他的公共模型类的话,可以使用如下方法:

$User = M('\Home\Model\CommonModel:User','think_','db_config');
// 相当于 $User = new \Home\Model\CommonModel('User','think_','db_config');

实例化空模型类

如果你仅仅是使用原生SQL查询的话,不需要使用额外的模型类,实例化一个空模型类即可进行操作了,例如:

//实例化空模型
$Model = new Model();
//或者使用M快捷方法是等效的
$Model = M();
//进行原生的SQL查询
$Model->query('SELECT * FROM think_user WHERE status = 1');

实例化空模型类后还可以用table方法切换到具体的数据表进行操作

字段定义

 

可以通过设置DB_FIELDS_CACHE 参数来关闭字段自动缓存,如果在开发的时候经常变动数据库的结构,而不希望进行数据表的字段缓存,可以在项目配置文件中增加如下配置:

// 关闭字段缓存
'DB_FIELDS_CACHE'=>false

注意:调试模式下面由于考虑到数据结构可能会经常变动,所以默认是关闭字段缓存的。

 

全局配置定义

 

常用的配置方式是在应用配置文件或者模块配置文件中添加下面的配置参数:

//数据库配置信息
'DB_TYPE'   => 'mysql', // 数据库类型
'DB_HOST'   => '127.0.0.1', // 服务器地址
'DB_NAME'   => 'thinkphp', // 数据库名
'DB_USER'   => 'root', // 用户名
'DB_PWD'    => '123456', // 密码
'DB_PORT'   => 3306, // 端口
'DB_PARAMS' =>  array(), // 数据库连接参数
'DB_PREFIX' => 'think_', // 数据库表前缀 
'DB_CHARSET'=> 'utf8', // 字符集
'DB_DEBUG'  =>  TRUE, // 数据库调试模式 开启后可以记录SQL日志

数据库的类型由DB_TYPE参数设置。

下面是目前支持的数据库设置:

DB_TYPE设置数据库类型
mysqlmysql
pgsqlpgsql
sqlitesqlite
sqlsrvsqlserver
oracleoracle
firebirdibase
mongomongo

连贯操作

WHERE

 

字符串条件

使用字符串条件直接查询和操作,例如:

$User = M("User"); // 实例化User对象
$User->where('type=1 AND status=1')->select(); 

数组条件

数组条件的where用法是ThinkPHP推荐的用法。

普通查询

最简单的数组查询方式如下:

$User = M("User"); // 实例化User对象
$map['name'] = 'thinkphp';
$map['status'] = 1;
// 把查询条件传入查询方法
$User->where($map)->select(); 

表达式查询

上面的查询条件仅仅是一个简单的相等判断,可以使用查询表达式支持更多的SQL查询语法,查询表达式的使用格式:

$map['字段1']  = array('表达式','查询条件1');
$map['字段2']  = array('表达式','查询条件2');
$Model->where($map)->select(); // 也支持

表达式不分大小写,支持的查询表达式有下面几种,分别表示的含义是:

表达式含义
EQ等于(=)
NEQ不等于(<>)
GT大于(>)
EGT大于等于(>=)
LT小于(<)
ELT小于等于(<=)
LIKE模糊查询
[NOT] BETWEEN(不在)区间查询
[NOT] IN(不在)IN 查询
EXP表达式查询,支持SQL语法

多次调用

where方法支持多次调用,但字符串条件只能出现一次,例如:

$map['a'] = array('gt',1);
$where['b'] = 1;
$Model->where($map)->where($where)->where('status=1')->select();

多次的数组条件表达式会最终合并,但字符串条件则只支持一次。

TABLE

 

ALIAS

alias用于设置当前数据表的别名,便于使用其他的连贯操作例如join方法等。

DATA

 

写操作

通常情况下我们都是通过create方法或者赋值的方式生成数据对象,然后写入数据库,例如:

$Model = D('User');
$Model->create();
// 这里略过具体的自动生成和验证判断
$Model->add();

data方法则是直接生成要操作的数据对象,例如:

$Model = M('User');
$data['name'] = '流年';
$data['email'] = 'thinkphp@qq.com';
$Model->data($data)->add();

读操作

除了写操作外,data方法还可以用于读取当前的数据对象,例如:

$User = M('User');
$map['name'] = '流年';
$User->where($map)->find();
// 读取当前数据对象
$data = $User->data(); 

FIELD

属于模型的连贯操作方法之一,主要目的是标识要返回或者操作的字段,可以用于查询和写入操作。

 

field方法的参数可以支持数组,例如:

$Model->field(array('id','title','content'))->select();

ORDER

 

order方法属于模型的连贯操作方法之一,用于对操作的结果排序。

用法如下:

$Model->where('status=1')->order('id desc')->limit(5)->select();

注意:连贯操作方法没有顺序,可以在select方法调用之前随便改变调用顺序。

支持对多个字段的排序,例如:

$Model->where('status=1')->order('id desc,status')->limit(5)->select();

如果没有指定desc或者asc排序规则的话,默认为asc。

LIMIT

limit方法也是模型类的连贯操作方法之一,主要用于指定查询和操作的数量,特别在分页查询的时候使用较多。

 

限制结果数量

例如获取满足要求的10个用户,如下调用即可:

$User = M('User');
$User->where('status=1')->field('id,name')->limit(10)->select();

limit方法也可以用于写操作,例如更新满足要求的3条数据:

$User = M('User');
$User->where('score=100')->limit(3)->save(array('level'=>'A'));

分页查询

用于文章分页查询是limit方法比较常用的场合,例如:

$Article = M('Article');
$Article->limit('10,25')->select();

表示查询文章数据,从第10行开始的25条数据(可能还取决于where条件和order排序的影响 这个暂且不提)。

你也可以这样使用,作用是一样的:

$Article = M('Article');
$Article->limit(10,25)->select();

对于大数据表,尽量使用limit限制查询结果,否则会导致很大的内存开销和性能问题。

PAGE

page方法也是模型的连贯操作方法之一,是完全为分页查询而诞生的一个人性化操作方法。

 

$Article = M('Article');
$Article->page('1,10')->select(); // 查询第一页数据
$Article->page('2,10')->select(); // 查询第二页数据

显而易见的是,使用page方法你不需要计算每个分页数据的起始位置,page方法内部会自动计算。

和limit方法一样,page方法也支持2个参数的写法

 

page方法还可以和limit方法配合使用,例如:

$Article->limit(25)->page(3)->select();

当page方法只有一个值传入的时候,表示第几页,而limit方法则用于设置每页显示的数量,也就是说上面的写法等同于:

$Article->page('3,25')->select(); 

GROUP

通常用于结合合计函数,根据一个或多个列对结果集进行分组 。

 

支持对多个字段进行分组,例如:

$this->field('user_id,test_time,username,max(score)')->group('user_id,test_time')->select();

HAVING

用于配合group方法完成从分组的结果中筛选(通常是聚合条件)数据。

having方法只有一个参数,并且只能使用字符串,例如:

$this->field('username,max(score)')->group('user_id')->having('count(test_time)>3')->select(); 

JOIN

用于根据两个或多个表中的列之间的关系,从这些表中查询数据。

join通常有下面几种类型,不同类型的join操作会影响返回的数据结果。

  • INNER JOIN: 等同于 JOIN(默认的JOIN类型),如果表中有至少一个匹配,则返回行
  • LEFT JOIN: 即使右表中没有匹配,也从左表返回所有的行
  • RIGHT JOIN: 即使左表中没有匹配,也从右表返回所有的行
  • FULL JOIN: 只要其中一个表中存在匹配,就返回行

join方法可以支持以上四种类型,例如:

$Model = M('Artist');
$Model
->join('think_work ON think_artist.id = think_work.artist_id')
->join('think_card ON think_artist.card_id = think_card.id')
->select();
$Model->join('__WORK__ ON __ARTIST__.id = __WORK__.artist_id','RIGHT')->select();

UNION

用于合并两个或多个 SELECT 语句的结果集。

 

$Model->field('name')
      ->table('think_user_0')
      ->union('SELECT name FROM think_user_1')
      ->union('SELECT name FROM think_user_2')
      ->select();

数组用法:

$Model->field('name')
      ->table('think_user_0')
      ->union(array('field'=>'name','table'=>'think_user_1'))
      ->union(array('field'=>'name','table'=>'think_user_2'))
      ->select();

支持UNION ALL 操作,例如:

$Model->field('name')
      ->table('think_user_0')
      ->union('SELECT name FROM think_user_1',true)
      ->union('SELECT name FROM think_user_2',true)
      ->select();
Union将会按照字段的顺序进行排序;UNION ALL只是简单的将两个结果合并后就返回。

每个union方法相当于一个独立的SELECT语句。

DISTINCT

用于返回唯一不同的值 (去重)。

例如:

$Model->distinct(true)->field('name')->select();

生成的SQL语句是: SELECT DISTINCT name FROM think_user

distinct方法的参数是一个布尔值。

LOCK

 

Lock方法是用于数据库的锁机制,如果在查询或者执行操作的时候使用:

lock(true);

就会自动在生成的SQL语句最后加上 FOR UPDATE或者FOR UPDATE NOWAIT(Oracle数据库)。

CACHE

 

cache方法用于查询缓存操作,也是连贯操作方法之一。

cache可以用于selectfindgetField方法,以及其衍生方法,使用cache方法后,在缓存有效期之内不会再次进行数据库查询操作,而是直接获取缓存中的数据,关于数据缓存的类型和设置可以参考缓存部分

例如,我们对find方法使用cache方法如下:

$Model = M('User');
$Model->where('id=5')->cache(true)->find();

COMMENT

 

COMMENT方法 用于在生成的SQL语句中添加注释内容,例如:

$this->comment('查询考试前十名分数')
->field('username,score')
->limit(10)
->order('score desc')
->select();

fetchSql

用于直接返回SQL而不是执行查询,适用于任何的CURD操作方法。 例如:

$result = M('User')->fetchSql(true)->find(1);

输出result结果为: SELECT * FROM think_user where id = 1

TOKEN

用于临时关闭令牌验证,例如:

$model->token(false)->create();

即可在提交表单的时候临时关闭令牌验证(即使开启了TOKEN_ON参数)。

STRICT

用于设置数据写入和查询是否严格检查是否存在字段。默认情况下不合法数据字段自动删除,如果设置了严格检查则会抛出异常。 例如:

$model->strict(true)->add($data);

命名范围

 

定义属性

要使用命名范围功能,主要涉及到模型类的_scope属性定义和scope连贯操作方法的使用。

定义_scope属性:

namespace Home\Model;
use Think\Model;
class NewsModel extends Model {
     protected $_scope = array(
         // 命名范围normal
         'normal'=>array(
             'where'=>array('status'=>1),
         ),
         // 命名范围latest
         'latest'=>array(
             'order'=>'create_time DESC',
             'limit'=>10,
         ),
     );
}

_scope属性是一个数组,每个数组项表示定义一个命名范围,命名范围的定义格式为:

'命名范围标识'=>array(
     '属性1'=>'值1',
     '属性2'=>'值2',

命名范围标识:可以是任意的字符串,用于标识当前定义的命名范围名称。

命名范围支持的属性包括:

属性描述
where查询条件
field查询字段
order结果排序
table查询表名
limit结果限制
page结果分页
havinghaving查询
groupgroup查询
lock查询锁定
distinct唯一查询
cache查询缓存

每个命名范围的定义可以包括这些属性中一个或者多个。

调用多个命名范围

也可以支持同时调用多个命名范围定义,例如:

$Model->scope('normal')->scope('latest')->select();

或者简化为:

$Model->scope('normal,latest')->select();

生成的SQL都是:

SELECT * FROM think_news WHERE status=1 ORDER BY create_time DESC LIMIT 10

如果两个命名范围的定义存在冲突,则后面调用的命名范围定义会覆盖前面的相同属性的定义

默认命名范围

系统支持默认命名范围功能,如果你定义了一个default命名范围,例如:

    protected $_scope = array(
         // 默认的命名范围
         'default'=>array(
             'where'=>array('status'=>1),
             'limit'=>10,
         ),
     );

那么调用default命名范围可以直接使用:

$Model->scope()->select();

而无需再传入命名范围标识名

$Model->scope('default')->select();

命名范围调整

如果你需要在normal命名范围的基础上增加额外的调整,可以使用:

$Model->scope('normal',array('limit'=>5))->select();

生成的SQL语句是:

SELECT * FROM think_news WHERE status=1 LIMIT 5

当然,也可以在两个命名范围的基础上进行调整,例如:

$Model->scope('normal,latest',array('limit'=>5))->select();

生成的SQL是:

SELECT * FROM think_news WHERE status=1 ORDER BY create_time DESC LIMIT 5

CURD操作

 

// 获取表单的POST数据
$data['name'] = $_POST['name'];
$data['email'] = $_POST['email'];
// 更多的表单数据值获取
//……

 

// 实例化User模型
$User = M('User');
// 根据表单提交的POST数据创建数据对象
$User->create();

Create方法支持从其它方式创建数据对象,例如,从其它的数据对象,或者数组等

$data['name'] = 'ThinkPHP';
$data['email'] = 'ThinkPHP@gmail.com';
$User->create($data);

创建完成的数据可以直接读取和修改,例如:

$data['name'] = 'ThinkPHP';
$data['email'] = 'ThinkPHP@gmail.com';
$User->create($data);
// 创建完成数据对象后可以直接读取数据
echo $User->name;
echo $User->email;
// 也可以直接修改创建完成的数据
$User->name = 'onethink'; // 修改name字段数据
$User->status = 1; // 增加新的字段数据

字段合法性过滤

如果在create方法之前调用field方法,则表示只允许创建指定的字段数据,其他非法字段将会被过滤,例如:

$data['name'] = 'thinkphp';
$data['email'] = 'thinkphp@gmail.com';
$data['status'] = 1;
$data['test'] = 'test';
$User = M('User');
$data = $User->field('name,email')->create($data);
dump($data);

输出结果为:

array (size=2)
  'name' => string 'thinkphp' (length=8)
  'email' => string 'thinkphp@gmail.com' (length=18)

最终只有nameemail字段的数据被允许写入,statustest字段直接被过滤了,哪怕status也是数据表中的合法字段

数据写入

 

ThinkPHP的数据写入操作使用add方法,使用示例如下:

$User = M("User"); // 实例化User对象
$data['name'] = 'ThinkPHP';
$data['email'] = 'ThinkPHP@gmail.com';
$User->add($data);

如果是Mysql数据库的话,还可以支持在数据插入时允许更新操作:

add($data='',$options=array(),$replace=false)

其中add方法增加$replace参数(是否添加数据时允许覆盖),true表示覆盖,默认为false

 

如果在add之前已经创建数据对象的话(例如使用了create或者data方法),add方法就不需要再传入数据了。 使用create方法的例子:

$User = M("User"); // 实例化User对象
// 根据表单提交的POST数据创建数据对象
if($User->create()){
    $result = $User->add(); // 写入数据到数据库 
    if($result){
        // 如果主键是自动增长型 成功后返回值就是最新插入的值
        $insertId = $result;
    }
}

create方法并不算是连贯操作,因为其返回值可能是布尔值,所以必须要进行严格判断。

字段过滤

如果写入了数据表中不存在的字段数据,则会被直接过滤,例如:

$data['name'] = 'thinkphp';
$data['email'] = 'thinkphp@gmail.com';
$data['test'] = 'test';
$User = M('User');
$User->data($data)->add();

其中test字段是不存在的,所以写入数据的时候会自动过滤掉。

在3.2.2版本以上,如果开启调试模式的话,则会抛出异常,提示:非法数据对象:[test=>test]

字段内容过滤

通过filter方法可以对数据的值进行过滤处理,例如:

$data['name'] = '<b>thinkphp</b>';
$data['email'] = 'thinkphp@gmail.com';
$User = M('User');
$User->data($data)->filter('strip_tags')->add();

写入数据库的时候会把name字段的值转化为thinkphp

filter方法的参数是一个回调类型,支持函数或者闭包定义。

批量写入

在某些情况下可以支持数据的批量写入,例如:

// 批量添加数据
$dataList[] = array('name'=>'thinkphp','email'=>'thinkphp@gamil.com');
$dataList[] = array('name'=>'onethink','email'=>'onethink@gamil.com');
$User->addAll($dataList);

该功能需要3.2.3以上版本,3.2.3以下版本仅对mysql数据库支持

数据读取

分为读取数据、读取数据集和读取字段值。

数据查询方法支持的连贯操作方法有:

连贯操作作用支持的参数类型
where用于查询或者更新条件的定义字符串、数组和对象
table用于定义要操作的数据表名称字符串和数组
alias用于给当前数据表定义别名字符串
field用于定义要查询的字段(支持字段排除)字符串和数组
order用于对结果排序字符串和数组
group用于对查询的group支持字符串
having用于对查询的having支持字符串
join用于对查询的join支持字符串和数组
union用于对查询的union支持字符串、数组和对象
distinct用于查询的distinct支持布尔值
lock用于数据库的锁机制布尔值
cache用于查询缓存支持多个参数
relation用于关联查询(需要关联模型支持)字符串
result用于返回数据转换字符串
scope用于命名范围字符串、数组
bind用于数据绑定操作数组
comment用于SQL注释字符串
fetchSql不执行SQL而只是返回SQL布尔值

读取数据行:

 

读取数据是指读取数据表中的一行数据(或者关联数据),主要通过find方法完成

 

读取数据集:

读取数据集其实就是获取数据表中的多行记录(以及关联数据),使用select方法,

读取字段值:

读取字段值其实就是获取数据表中的某个列的多个或者单个数据,最常用的方法是 getField方法。

 

如果传入多个字段的话,默认返回一个关联数组:

$User = M("User"); // 实例化User对象
// 获取所有用户的ID和昵称列表 
$list = $User->getField('id,nickname');
//两个字段的情况下返回的是array(`id`=>`nickname`)的关联数组,以id的值为key,nickname字段值为value

这样返回的list是一个数组,键名是用户的id字段的值,键值是用户的昵称nickname。

getField方法还可以支持限制数量,例如:

$this->getField('id,name',5); // 限制返回5条记录
$this->getField('id',3); // 获取id数组 限制3条记录

数据更新

 

更新数据使用save方法,例如:

$User = M("User"); // 实例化User对象
// 要修改的数据对象属性赋值
$data['name'] = 'ThinkPHP';
$data['email'] = 'ThinkPHP@gmail.com';
$User->where('id=5')->save($data); // 根据条件更新记录

 

更新字段

如果只是更新个别字段的值,可以使用setField方法。

使用示例:

$User = M("User"); // 实例化User对象
// 更改用户的name值
$User-> where('id=5')->setField('name','ThinkPHP');

setField方法支持同时更新多个字段,只需要传入数组即可,例如:

$User = M("User"); // 实例化User对象
// 更改用户的name和email的值
$data = array('name'=>'ThinkPHP','email'=>'ThinkPHP@gmail.com');
$User-> where('id=5')->setField($data);

对于统计字段(通常指的是数字类型)的更新,系统还提供了setIncsetDec方法。

$User = M("User"); // 实例化User对象
$User->where('id=5')->setInc('score',3); // 用户的积分加3
$User->where('id=5')->setInc('score'); // 用户的积分加1
$User->where('id=5')->setDec('score',5); // 用户的积分减5
$User->where('id=5')->setDec('score'); // 用户的积分减1

3.2.3版本开始,setInc和setDec方法支持延迟更新,用法如下:

$Article = M("Article"); // 实例化Article对象
$Article->where('id=5')->setInc('view',1); // 文章阅读数加1
$Article->where('id=5')->setInc('view',1,60); // 文章阅读数加1,并且延迟60秒更新(写入)

数据删除

 

ThinkPHP删除数据使用delete方法,例如:

$Form = M('Form');
$Form->delete(5);

表示删除主键为5的数据,delete方法可以删除单个数据,也可以删除多个数据,这取决于删除条件,例如:

$User = M("User"); // 实例化User对象
$User->where('id=5')->delete(); // 删除id为5的用户数据
$User->delete('1,2,5'); // 删除主键为1,2和5的用户数据
$User->where('status=0')->delete(); // 删除所有状态为0的用户数据

delete方法的返回值是删除的记录数,如果返回值是false则表示SQL出错,返回值如果为0表示没有删除任何数据。

也可以用order和limit方法来限制要删除的个数,例如:

// 删除所有状态为0的5 个用户数据 按照创建时间排序
$User->where('status=0')->order('create_time')->limit('5')->delete(); 

查询语言

 

1、使用字符串作为查询条件,数组查询,对象查询

如果进行多字段查询,那么字段之间的默认逻辑关系是 逻辑与 AND,但是用下面的规则可以更改默认的逻辑判断,通过使用 _logic 定义查询逻辑:

$User = M("User"); // 实例化User对象
$condition['name'] = 'thinkphp';
$condition['account'] = 'thinkphp';
$condition['_logic'] = 'OR';
// 把查询条件传入查询方法
$User->where($condition)->select(); 

最后生成的SQL语句是

SELECT * FROM think_user WHERE `name`='thinkphp' OR `account`='thinkphp'

以stdClass内置对象为例:

$User = M("User"); // 实例化User对象
// 定义查询条件
$condition = new stdClass(); 
$condition->name = 'thinkphp'; 
$condition->status= 1; 
$User->where($condition)->select(); 

使用数组和对象方式查询的时候,如果传入了不存在的查询字段是会被自动过滤的,例如:

$User = M("User"); // 实例化User对象
$condition['name'] = 'thinkphp';
$condition['status'] = 1;
$condition['test'] = 'test';
// 把查询条件传入查询方法
$User->where($condition)->select(); 

因为数据库的test字段是不存在的,所以系统会自动检测并过滤掉$condition['test'] = 'test'这一查询条件。

2、表达式查询

使用格式:

$map['字段名'] = array('表达式','查询条件');

表达式不分大小写,支持的查询表达式有下面几种,分别表示的含义是:

表达式含义协助记忆
EQ等于(=)equal
NEQ不等于(<>)not equal
GT大于(>)greater
EGT大于等于(>=)equal or greater
LT小于(<)less than
ELT小于等于(<=)equal or less than
LIKE模糊查询 
[NOT] BETWEEN(不在)区间查询 
[NOT] IN(不在)IN 查询 
EXP表达式查询,支持SQL语法expression

例如:

$map['id']  = array('eq',100);

和下面的查询等效

$map['id']  = 100;

3、快捷查询

快捷查询方式是一种多字段查询的简化写法,可以进一步简化查询条件的写法,在多个字段之间用**|分割表示OR查询,用&**分割表示AND查询,可以实现下面的查询,例如:

不同字段相同的查询条件

$User = M("User"); // 实例化User对象
$map['name|title'] = 'thinkphp';
// 把查询条件传入查询方法
$User->where($map)->select(); 

不同字段不同的查询条件

$User = M("User"); // 实例化User对象
$map['status&title'] =array('1','thinkphp','_multi'=>true);
// 把查询条件传入查询方法
$User->where($map)->select(); 

上面的查询等效于:

$User = M("User"); // 实例化User对象
$map['status'] = 1;
$map['title']  = 'thinkphp';
// 把查询条件传入查询方法
$User->where($map)->select(); 

'_multi'=>true必须加在数组的最后,表示当前是多条件匹配,这样查询条件就变成 status= 1 AND title = 'thinkphp'

4、区间查询

ThinkPHP支持对某个字段的区间查询,例如:

$map['id'] = array(array('gt',1),array('lt',10)) ;

得到的查询条件是: ( id > 1) AND ( id < 10)

$map['id'] = array(array('gt',3),array('lt',10), 'or') ;

得到的查询条件是: ( id > 3) OR ( id < 10)

$map['id']  = array(array('neq',6),array('gt',3),'and'); 

得到的查询条件是:( id != 6) AND ( id > 3)

最后一个可以是AND、 OR或者 XOR运算符,如果不写,默认是AND运算。

5、组合查询

组合查询的主体还是采用数组方式查询,只是加入了一些特殊的查询支持,包括字符串模式查询(_string)、复合查询(_complex)、请求字符串查询(_query),混合查询中的特殊查询每次查询只能定义一个,由于采用数组的索引方式,索引相同的特殊查询会被覆盖。

字符串模式查询

数组条件可以和字符串条件(采用_string 作为查询条件)混合使用,例如:

$User = M("User"); // 实例化User对象
$map['id'] = array('neq',1);
$map['name'] = 'ok';
$map['_string'] = 'status=1 AND score>10';
$User->where($map)->select(); 

最后得到的查询条件就成了:

( `id` != 1 ) AND ( `name` = 'ok' ) AND ( status=1 AND score>10 )

请求字符串查询方式

请求字符串查询是一种类似于URL传参的方式,可以支持简单的条件相等判断。

$map['id'] = array('gt','100');
$map['_query'] = 'status=1&score=100&_logic=or';

得到的查询条件是:

`id`>100 AND (`status` = '1' OR `score` = '100')

复合查询

复合查询相当于封装了一个新的查询条件,然后并入原来的查询条件之中,所以可以完成比较复杂的查询条件组装。 例如:

$where['name']  = array('like', '%thinkphp%');
$where['title']  = array('like','%thinkphp%');
$where['_logic'] = 'or';
$map['_complex'] = $where;
$map['id']  = array('gt',1);

查询条件是

( id > 1) AND ( ( name like '%thinkphp%') OR ( title like '%thinkphp%') )

6、统计查询

在应用中我们经常会用到一些统计数据,例如当前所有(或者满足某些条件)的用户数、所有用户的最大积分、用户的平均成绩等等,ThinkPHP为这些统计操作提供了一系列的内置方法,包括:

方法说明
Count统计数量,参数是要统计的字段名(可选)
Max获取最大值,参数是要统计的字段名(必须)
Min获取最小值,参数是要统计的字段名(必须)
Avg获取平均值,参数是要统计的字段名(必须)
Sum获取总分,参数是要统计的字段名(必须)
$User = M("User"); // 实例化User对象

获取用户数:

$userCount = $User->count();

或者根据字段统计:

$userCount = $User->count("id");

获取用户的最大积分:

$maxScore = $User->max('score');

获取积分大于0的用户的最小积分:

$minScore = $User->where('score>0')->min('score');

获取用户的平均积分:

$avgScore = $User->avg('score');

统计用户的总成绩:

$sumScore = $User->sum('score');

并且所有的统计查询均支持连贯操作的使用。

7、SQL查询

QUERY方法

query方法用于执行SQL查询操作,如果数据非法或者查询错误则返回false,否则返回查询结果数据集(同select方法)。

使用示例:

$Model = new \Think\Model() // 实例化一个model对象 没有对应任何数据表
$Model->query("select * from think_user where status=1");

EXECUTE方法

execute用于更新和写入数据的sql操作,如果数据非法或者查询错误则返回false ,否则返回影响的记录数。

使用示例:

$Model = new \Think\Model() // 实例化一个model对象 没有对应任何数据表
$Model->execute("update think_user set name='thinkPHP' where status=1");

8、动态查询

借助PHP5语言的特性,ThinkPHP实现了动态查询,核心模型的动态查询方法包括下面几种:

方法名说明举例
getBy根据字段的值查询数据例如,getByName,getByEmail
getFieldBy根据字段查询并返回某个字段的值例如,getFieldByName

 

getBy动态查询

该查询方式针对数据表的字段进行查询。例如,User对象拥有id,name,email,address 等属性,那么我们就可以使用下面的查询方法来直接根据某个属性来查询符合条件的记录。

$user = $User->getByName('liu21st');
$user = $User->getByEmail('liu21st@gmail.com');
$user = $User->getByAddress('中国深圳');

暂时不支持多数据字段的动态查询方法,请使用find方法和select方法进行查询。

getFieldBy动态查询

针对某个字段查询并返回某个字段的值,例如

$userId = $User->getFieldByName('liu21st','id');

表示根据用户的name获取用户的id值。

9、子查询

 

1、使用select方法 当select方法的参数为false的时候,表示不进行查询只是返回构建SQL,例如:

// 首先构造子查询SQL 
$subQuery = $model->field('id,name')->table('tablename')->group('field')->where($where)->order('status')->select(false); 

当select方法传入false参数的时候,表示不执行当前查询,而只是生成查询SQL。

2、使用buildSql方法

$subQuery = $model->field('id,name')->table('tablename')->group('field')->where($where)->order('status')->buildSql(); 

调用buildSql方法后不会进行实际的查询操作,而只是生成该次查询的SQL语句(为了避免混淆,会在SQL两边加上括号),然后我们直接在后续的查询中直接调用。

// 利用子查询进行查询 
$model->table($subQuery.' a')->where()->order()->select() 

构造的子查询SQL可用于ThinkPHP的连贯操作方法,例如table where等。

自动验证

 

验证规则

数据验证可以进行数据类型、业务规则、安全判断等方面的验证操作。

数据验证有两种方式:

  1. 静态方式:在模型类里面通过$_validate属性定义验证规则。
  2. 动态方式:使用模型类的validate方法动态创建自动验证规则。

无论是什么方式,验证规则的定义是统一的规则,定义格式为:

array(
     array(验证字段1,验证规则,错误提示,[验证条件,附加规则,验证时间]),
     array(验证字段2,验证规则,错误提示,[验证条件,附加规则,验证时间]),
     ......
);

说明

 

验证字段 (必须)

需要验证的表单字段名称,这个字段不一定是数据库字段,也可以是表单的一些辅助字段,例如确认密码和验证码等等。有个别验证规则和字段无关的情况下,验证字段是可以随意设置的,例如expire有效期规则是和表单字段无关的。如果定义了字段映射的话,这里的验证字段名称应该是实际的数据表字段而不是表单字段。

验证规则 (必须)

要进行验证的规则,需要结合附加规则,如果在使用正则验证的附加规则情况下,系统还内置了一些常用正则验证的规则,可以直接作为验证规则使用,包括:require 字段必须、email 邮箱、url URL地址、currency 货币、number 数字。

提示信息 (必须)

用于验证失败后的提示信息定义

验证条件 (可选)

包含下面几种情况:

  • self::EXISTS_VALIDATE 或者0 存在字段就验证(默认)
  • self::MUST_VALIDATE 或者1 必须验证
  • self::VALUE_VALIDATE或者2 值不为空的时候验证

附加规则(可选)

regex正则验证,定义的验证规则是一个正则表达式(默认)

 

验证时间(可选)

  • self::MODEL_INSERT或者1新增数据时候验证
  • self::MODEL_UPDATE或者2编辑数据时候验证
  • self::MODEL_BOTH或者3全部情况下验证(默认)

这里的验证时间需要注意,并非只有这三种情况,你可以根据业务需要增加其他的验证时间。

静态定义

在模型类里面预先定义好该模型的自动验证规则,我们称为静态定义。

举例说明,我们在模型类里面定义了$_validate属性如下:

namespace Home\Model;
use Think\Model;
class UserModel extends Model{
   protected $_validate = array(
     array('verify','require','验证码必须!'), //默认情况下用正则进行验证
     array('name','','帐号名称已经存在!',0,'unique',1), // 在新增的时候验证name字段是否唯一
     array('value',array(1,2,3),'值的范围不正确!',2,'in'), // 当值不为空的时候判断是否在一个范围内
     array('repassword','password','确认密码不正确',0,'confirm'), // 验证确认密码是否和密码一致
     array('password','checkPwd','密码格式不正确',0,'function'), // 自定义函数验证密码格式
   );
}

定义好验证规则后,就可以在使用create方法创建数据对象的时候自动调用:

$User = D("User"); // 实例化User对象
if (!$User->create()){
     // 如果创建失败 表示验证没有通过 输出错误提示信息
     exit($User->getError());
}else{
     // 验证通过 可以进行其他数据操作
}

在进行自动验证的时候,系统会对定义好的验证规则进行依次验证。如果某一条验证规则没有通过,则会报错,getError方法返回的错误信息(字符串)就是对应字段的验证规则里面的错误提示信息。

静态定义方式因为必须定义模型类,所以只能用D函数实例化模型

默认情况下,create方法是对表单提交的POST数据进行自动验证,如果你的数据来源不是表单post,仍然也可以进行自动验证,方法改进如下:

$User = D("User"); // 实例化User对象
$data = getData(); // 通过getData方法获取数据源的(数组)数据
if (!$User->create($data)){
     // 对data数据进行验证
     exit($User->getError());
}else{
     // 验证通过 可以进行其他数据操作

create方法的第二个参数就用于指定自动验证规则中的验证时间,也就是说create方法的自动验证只会验证符合验证时间的验证规则。

我们在上面提到这里的验证时间并非只有这几种情况,你可以根据业务需要增加其他的验证时间,例如,你可以给登录操作专门指定验证时间为4。我们定义验证规则如下:

namespace Home\Model;
use Think\Model;
class UserModel extends Model{
   protected $_validate = array(
     array('verify','require','验证码必须!'),  // 都有时间都验证
     array('name','checkName','帐号错误!',1,'function',4),  // 只在登录时候验证
     array('password','checkPwd','密码错误!',1,'function',4), // 只在登录时候验证
   );
}

那么,我们就可以在登录的时候使用

$User = D("User"); // 实例化User对象
if (!$User->create($_POST,4)){ // 登录验证数据
     // 验证没有通过 输出错误提示信息
     exit($User->getError());
}else{
     // 验证通过 执行登录操作
}

动态验证

 

如果采用动态验证的方式,就比较灵活,可以根据不同的需要,在操作同一个模型的时候使用不同的验证规则,例如上面的静态验证方式可以改为:

$rules = array(
     array('verify','require','验证码必须!'), //默认情况下用正则进行验证
     array('name','','帐号名称已经存在!',0,'unique',1), // 在新增的时候验证name字段是否唯一
     array('value',array(1,2,3),'值的范围不正确!',2,'in'), // 当值不为空的时候判断是否在一个范围内
     array('repassword','password','确认密码不正确',0,'confirm'), // 验证确认密码是否和密码一致
     array('password','checkPwd','密码格式不正确',0,'function'), // 自定义函数验证密码格式
);

$User = M("User"); // 实例化User对象
if (!$User->validate($rules)->create()){
     // 如果创建失败 表示验证没有通过 输出错误提示信息
     exit($User->getError());
}else{
     // 验证通过 可以进行其他数据操作
}

动态验证不依赖模型类的定义,所以通常用M函数实例化模型就可以

错误信息多语言支持

如果你希望支持多语言的错误信息提示,那么可以在验证规则里面如下定义:

protected $_validate = array(
     array('verify','require','{%VERIFY_CODE_MUST}'), 
     array('name','','{%ACCOUNT_EXISTS}',0,'unique',1), 
);

其中VERIFY_CODE_MUSTACCOUNT_EXISTS是我们在语言包里面定义的多语言变量。

如果是采用动态验证方式,则比较简单,直接在定义验证规则的时候使用L方法即可,例如:

$rules = array(
     array('verify','require',L('VERIFY_CODE_MUST')), 
     array('name','',L('ACCOUNT_EXISTS'),0,'unique',1), 
);

批量验证

系统支持数据的批量验证功能,只需要在模型类里面设置patchValidate属性为true( 默认为false),

protected $patchValidate = true;

设置批处理验证后,getError() 方法返回的错误信息是一个数组,返回格式是:

array("字段名1"=>"错误提示1","字段名2"=>"错误提示2"... )

前端可以根据需要需要自行处理,例如转换成json格式返回:

$User = D("User"); // 实例化User对象
if (!$User->create()){
     // 如果创建失败 表示验证没有通过 输出错误提示信息
     $this->ajaxReturn($User->getError());
}else{
     // 验证通过 可以进行其他数据操作
}

自动完成

 

自动完成是ThinkPHP提供用来完成数据自动处理和过滤的方法,使用create方法创建数据对象的时候会自动完成数据处理。

因此,在ThinkPHP使用create方法来创建数据对象是更加安全的方式,而不是直接通过add或者save方法实现数据写入。

规则定义

自动完成通常用来完成默认字段写入,安全字段过滤以及业务逻辑的自动处理等,和自动验证的定义方式类似,自动完成的定义也支持静态定义和动态定义两种方式。

  1. 静态方式:在模型类里面通过$_auto属性定义处理规则。
  2. 动态方式:使用模型类的auto方法动态创建自动处理规则。

两种方式的定义规则都采用:

array(
     array(完成字段1,完成规则,[完成条件,附加规则]),
     array(完成字段2,完成规则,[完成条件,附加规则]),
     ......
);

说明

完成字段(必须)

需要进行处理的数据表实际字段名称。

完成规则(必须)

需要处理的规则,配合附加规则完成。

完成时间(可选)

设置自动完成的时间,包括:

设置说明
self::MODEL_INSERT或者1新增数据的时候处理(默认)
self::MODEL_UPDATE或者2更新数据的时候处理
self::MODEL_BOTH或者3所有情况都进行处理

附加规则(可选)

包括:

规则说明
function使用函数,表示填充的内容是一个函数名
callback回调方法 ,表示填充的内容是一个当前模型的方法
field用其它字段填充,表示填充的内容是一个其他字段的值
string字符串(默认方式)
ignore为空则忽略(3.1.2新增)

静态定义

预先在模型类里面定义好自动完成的规则,我们称之为静态定义。例如,我们在模型类定义_auto属性:

namespace Home\Model;
use Think\Model;
class UserModel extends Model{
     protected $_auto = array ( 
         array('status','1'),  // 新增的时候把status字段设置为1
         array('password','md5',3,'function') , // 对password字段在新增和编辑的时候使md5函数处理
         array('name','getName',3,'callback'), // 对name字段在新增和编辑的时候回调getName方法
         array('update_time','time',2,'function'), // 对update_time字段在更新的时候写入当前时间戳
     );
}

然后,就可以在使用create方法创建数据对象的时候自动处理:

$User = D("User"); // 实例化User对象
if (!$User->create()){ // 创建数据对象
     // 如果创建失败 表示验证没有通过 输出错误提示信息
     exit($User->getError());
}else{
     // 验证通过 写入新增数据
     $User->add();
}

 

如果你没有定义任何自动验证规则的话,则不需要判断create方法的返回值

动态完成

除了静态定义之外,我们也可以采用动态完成的方式来解决不同的处理规则。

$rules = array ( 
    array('status','1'),  // 新增的时候把status字段设置为1
    array('password','md5',3,'function') , // 对password字段在新增和编辑的时候使md5函数处理
    array('update_time','time',2,'function'), // 对update_time字段在更新的时候写入当前时间戳
);
$User = M('User');
$User->auto($rules)->create();
$User->add();

参数绑定

指绑定一个参数到预处理的SQL语句中的对应命名占位符或问号占位符指定的变量,并且可以提高SQL处理的效

 

手动绑定

参数手动绑定需要调用连贯操作的bind方法,例如:

$Model = M('User');
$where['name'] = ':name';
$list = $Model->where($where)->bind(':name',I('name'))->select();

目前不支持?方式进行占位符,统一使用 :var 方式进行占位符,驱动内部会自动进行处理。

参数绑定的参数可以是条件或者要data数据中的参数,CURD操作均可以支持参数绑定bind方法。

自动绑定

对于某些操作的情况(例如模型的写入和更新方法),采用了参数的自动绑定

$Model = M('User');
$Model->name = 'thinkphp';
$Model->email = 'thinkphp@qq.com';
$Model->add();

会自动对写入的数据进行参数绑定操作。其操作等效于:

$Model = M('User');
$Model->name = ':name';
$Model->email = ':email';
$bind[':name']    = 'thinkphp';
$bind[':email']   = 'thinkphp@qq.com';
$Model->bind($bind)->add();

自动绑定不支持参数类型等额外设置,如果有必要请使用上面的手动绑定方式。

模型分层

把对用户表的所有模型操作分成三层:

  • 数据层:Home\Model\UserModel 用于定义数据相关的自动验证和自动完成和数据存取接口
  • 逻辑层:Home\Logic\UserLogic 用于定义用户相关的业务逻辑
  • 服务层:Home\Service\UserService 用于定义用户相关的服务接口等

三个模型层的定义如下:

Model类:Home\Model\UserModel.class.php

namespace Home\Model;
class UserModel extends \Think\Model{

}

实例化方法:D('User');

Logic类:Home\Logic\UserLogic.class.php

namespace Home\Logic;
class UserLogic extends \Think\Model{

}

实例化方法:D('User','Logic');

Api类:Home\Api\UserApi.class.php

namespace Home\Api;
class UserApi extends \Think\Model{

}

实例化方法:D('User','Api');

D方法默认操作的模型层由DEFAULT_M_LAYER参数配置,我们可以改变默认操作的模型层为Logic层,例如:

'DEFAULT_M_LAYER'       =>  'Logic', // 默认的模型层名称

这样,当我们调用:

$User = D('User');

的时候其实是实例化的 UserLogic类,而不是UserModel类。

视图模型

要定义视图模型,只需要继承Think\Model\ViewModel,然后设置viewFields属性即可。

例如下面的例子,我们定义了一个BlogView模型对象,其中包括了Blog模型的id、name、title和User模型的name,以及Category模型的title字段,我们通过创建BlogView模型来快速读取一个包含了User名称和类别名称的Blog记录(集)。

namespace Home\Model;
use Think\Model\ViewModel;
class BlogViewModel extends ViewModel {
   public $viewFields = array(
     'Blog'=>array('id','name','title'),
     'Category'=>array('title'=>'category_name', '_on'=>'Blog.category_id=Category.id'),
     'User'=>array('name'=>'username', '_on'=>'Blog.user_id=User.id'),
   );
 }

$viewFields 属性表示视图模型包含的字段,每个元素定义了某个数据表或者模型的字段。

 

视图查询

 

$Model->field('id,name,title,category_name,username')->order('id desc')->group('id')->select();

我们可以看到,即使不定义视图模型,其实我们也可以通过方法来操作,但是显然非常繁琐。

$Model = D("Blog");
$Model->table('think_blog Blog,think_category Category,think_user User')
 ->field('Blog.id,Blog.name,Blog.title,Category.title as category_name,User.name as username')
 ->order('Blog.id desc')
 ->where('Blog.category_id=Category.id AND Blog.user_id=User.id')
 ->select(

关联模型

关联关系

通常我们所说的关联关系包括下面三种:

一对一关联 :ONE_TO_ONE,包括HAS_ONE 和 BELONGS_TO 
一对多关联 :ONE_TO_MANY,包括HAS_MANY 和 BELONGS_TO
多对多关联 :MANY_TO_MANY

关联关系必然有一个参照表,例如:

  • 有一个员工档案管理系统项目,这个项目要包括下面的一些数据表:基本信息表、员工档案表、部门表、项目组表、银行卡表(用来记录员工的银行卡资料)。
  • 这些数据表之间存在一定的关联关系,我们以员工基本信息表为参照来分析和其他表之间的关联:
  • 每个员工必然有对应的员工档案资料,所以属于HAS_ONE关联;
  • 每个员工必须属于某个部门,所以属于BELONGS_TO关联;
  • 每个员工可以有多个银行卡,但是每张银行卡只可能属于一个员工,因此属于HAS_MANY关联;
  • 每个员工可以同时在多个项目组,每个项目组同时有多个员工,因此属于MANY_TO_MANY关联;
  • 分析清楚数据表之前的关联关系后,我们才可以进行关联定义和关联操作。

关联定义

ThinkPHP可以很轻松的完成数据表的关联CURD操作,目前支持的关联关系包括下面四种:

HAS_ONEBELONGS_TOHAS_MANYMANY_TO_MANY

模型类必须继承Think\Model\RelationModel类,关联定义的格式是:

namespace Home\Model;
use Think\Model\RelationModel;
class UserModel extends RelationModel{
    protected $_link = array(
         '关联1'  =>  array(
             '关联属性1' => '定义',
             '关联属性N' => '定义',
         ),
         '关联2'  =>  array(
             '关联属性1' => '定义',
             '关联属性N' => '定义',
         ),
         '关联3'  =>  HAS_ONE, // 快捷定义
         ...
    );
}

HAS_ONE

HAS_ONE关联表示当前模型拥有一个子对象,例如,每个员工都有一个人事档案。我们可以建立一个用户模型UserModel,并且添加如下关联定义:

namespace Home\Model;
use Think\Model\RelationModel;
class UserModel extends RelationModel{
     protected $_link = array(
        'Profile'=> self::HAS_ONE,
     );
}

BELONGS_TO

Belongs_to 关联表示当前模型从属于另外一个父对象,例如每个用户都属于一个部门。我们可以做如下关联定义。

'Dept' => self::BELONGS_TO

HAS_MANY

HAS_MANY 关联表示当前模型拥有多个子对象,例如每个用户有多篇文章,我们可以这样来定义:

'Article' => self::HAS_MANY

MANY_TO_MANY

MANY_TO_MANY 关联表示当前模型可以属于多个对象,而父对象则可能包含有多个子对象,通常两者之间需要一个中间表类约束和关联。例如每个用户可以属于多个组,每个组可以有多个用户:

'Group' => self::MANY_TO_MANY

关联查询

由于性能问题,新版取消了自动关联查询机制,而统一使用relation方法进行关联操作,relation方法不但可以启用关联还可以控制局部关联操作,实现了关联操作一切尽在掌握之中。

$User = D("User");
$user = $User->relation(true)->find(1);

输出$user结果可能是类似于下面的数据:

array(
    'id'        => 1,
    'account'   => 'ThinkPHP',
    'password'  => '123456',
    'Profile'   => array(
        'email'     => 'liu21st@gmail.com',
        'nickname'  => '流年',
        ),
    )

关联操作

除了关联查询外,系统也支持关联数据的自动写入、更新和删除

关联写入

$User = D("User");
$data = array();
$data["account"]    = "ThinkPHP";
$data["password"]   = "123456";
$data["Profile"]    = array(
  'email'    =>'liu21st@gmail.com',
  'nickname'    =>'流年',
);
$result = $User->relation(true)->add($data);

这样就会自动写入关联的Profile数据。

同样,可以使用参数来控制要关联写入的数据:

$result = $User->relation("Profile")->add($data);

当MANY_TO_MANY时,不建议使用关联插入。

关联更新

数据的关联更新和关联写入类似

$User = D("User");
$data["account"]    = "ThinkPHP";
$data["password"]   = "123456";
$data["Profile"]    = array(
     'email'    =>'liu21st@gmail.com',
     'nickname'    =>'流年',
);
$result = $User-> relation(true)->where(array('id'=>3))->save($data);

Relation(true)会关联保存User模型定义的所有关联数据,如果只需要关联保存部分数据,可以使用:

$result = $User->relation("Profile")->save($data);

这样就只会同时更新关联的Profile数据。

关联保存的规则:

HAS_ONE: 关联数据的更新直接赋值

HAS_MANY: 的关联数据如果传入主键的值 则表示更新 否则就表示新增

MANY_TO_MANY: 的数据更新是删除之前的数据后重新写入

关联删除

//删除用户ID为3的记录的同时删除关联数据
$result = $User->relation(true)->delete("3");
// 如果只需要关联删除部分数据,可以使用
$result = $User->relation("Profile")->delete("3");

高级模型

需要继承Think\Model\AdvModel类或者采用动态模型。

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

字段过滤

 

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

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

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

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

其中,contentWriteFilter是自定义的对字符串进行安全过滤的函数,而contentReadFilter是自定义的一个对内容进行截取的函数

 

 

 

序列化字段

要使用序列化字段的功能,只需要在模型中定义serializeField属性,定义格式如下:

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

文本字段

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

Protected  $blobFields = array('content');

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

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

 

只读字段

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

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

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

 

悲观锁( Pessimistic Locking )

悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。

 

 

ThinkPHP支持悲观锁机制,默认情况下,是关闭悲观锁功能的,要在查询和更新的时候启用悲观锁功能,可以通过使用之前提到的查询锁定方法,例如:

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

乐观锁( Optimistic Locking )

数据分表

在需要分表的模型中定义partition属性即可。

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

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

 

返回类型

系统默认的数据库查询返回的是数组,我们可以给单个数据设置返回类型,以满足特殊情况的需要,例如:

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

返回自定义的User类,类的架构方法的参数是传入的数据。例如:

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

Mongo模型(thinkphp 3.2.3手册)

Mongo模型是专门为Mongo数据库驱动而支持的Model扩展,如果需要操作Mongo数据库的话,自定义的模型类必须继承Think\Model\MongoModel。

 

Mongo模型为操作Mongo数据库提供了更方便的实用功能和查询用法,包括:

  1. 对MongoId对象和非对象主键的全面支持;
  2. 保持了动态追加字段的特性;
  3. 数字自增字段的支持;
  4. 执行SQL日志的支持;
  5. 字段自动检测的支持;
  6. 查询语言的支持;
  7. MongoCode执行的支持;

主键

系统很好的支持Mongo的主键类型,Mongo默认的主键名是 _id,也可以通过设置pk属性改变主键名称(也许你需要用其他字段作为数据表的主键),例如:

namespace Home\Model;
use Think\Model\MongoModel;
Class UserModel extends MongoModel {
    Protected $pk = 'id';
}

主键支持三种类型(通过_idType属性设置),分别是:

类型描述
self::TYPE_OBJECT或者1(默认类型) 采用MongoId对象,写入或者查询的时候传入数字或者字符会自动转换,获取的时候会自动转换成字符串。
self::TYPE_INT或者2整形,支持自动增长,通过设置_autoInc 属性
self::TYPE_STRING或者3字符串hash

设置主键类型示例:

namespace Home\Model;
use Think\Model\MongoModel;
Class UserModel extends MongoModel {
     Protected $_idType = self::TYPE_INT;
     protected $_autoinc =  true;
}

连贯操作

MongoModel中有部分连贯操作暂时不支持,包括:group、union、join、having、lock和distinct操作。其他连贯操作都可以很好的支持,例如:

$Model = new Think\Model\MongoModel("User");
$Model->field("name,email,age")->order("status desc")->limit("10,8")->select();

查询支持

Mongo数据库的查询条件和其他数据库有所区别。

  1. 首先,支持所有的普通查询和快捷查询;
  2. 表达式查询增加了一些针对MongoDb的查询用法;
  3. 统计查询目前只能支持count操作,其他的可能要自己通过MongoCode来实现了;

MongoModel的组合查询支持

_string 采用MongoCode查询
_query 和其他数据库的请求字符串查询相同
_complex MongoDb暂不支持

MongoModel提供了MongoCode方法,可以支持MongoCode方式的查询或者操作。

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值