基本概念

模型会自动对应数据表,模型类的命名规则是除去表前缀的数据表名称,采用驼峰法命名,并且首字母大写,例如:

| 模型名称 | 约定对应数据表(假设数据库的前缀定义是 think_) | | —- | —- |

| User | think_user |

| UserType | think_user_type |

如果你的规则和上面的系统约定不符合,那么需要设置Model类的数据表名称属性,以确保能够找到对应的数据表。
模型自动对应的数据表名称都是遵循小写+下划线规范,如果你的表名有大写的情况,必须通过设置模型的table属性。

如果你希望给模型类添加后缀,需要设置name属性或者table属性。

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class UserModel extends Model
  5. {
  6. protected $name = 'user';
  7. }

模型设置

默认主键为id,如果你没有使用id作为主键名,需要在模型中设置属性:

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class User extends Model
  5. {
  6. protected $pk = 'uid';
  7. }

如果你想指定数据表甚至数据库连接的话,可以使用:

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class User extends Model
  5. {
  6. // 设置当前模型对应的完整数据表名称
  7. protected $table = 'think_user';
  8. // 设置当前模型的数据库连接
  9. protected $connection = 'db_config';
  10. }

connection属性使用用配置参数名(需要在数据库配置文件中的connections参数中添加对应标识)。
常用的模型设置属性包括(以下属性都不是必须设置):

| 属性 | 描述 | | —- | —- |

| name | 模型名(相当于不带数据表前后缀的表名,默认为当前模型类名) |

| table | 数据表名(默认自动获取) |

| suffix | 数据表后缀(默认为空) |

| pk | 主键名(默认为id) |

| connection | 数据库连接(默认读取数据库配置) |

| query | 模型使用的查询类名称 |

| field | 模型允许写入的字段列表(数组) |

| schema | 模型对应数据表字段及类型 |

| type | 模型需要自动转换的字段及类型 |

| strict | 是否严格区分字段大小写(默认为true) |

| disuse | 数据表废弃字段(数组) |

| readonly | 只读字段(数组) |

| json | JSON字段(数组) |

模型不支持对数据表的前缀单独设置,并且也不推荐使用数据表的前缀设计,应该用不同的库区分。当你的数据表没有前缀的时候,nametable属性的定义是没有区别的,定义任何一个即可。

模型字段

模型的数据字段和对应数据表的字段是对应的,默认会自动获取(包括字段类型),但自动获取会导致增加一次查询,因此你可以在模型中明确定义字段信息避免多一次查询的开销。

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class User extends Model
  5. {
  6. // 设置字段信息
  7. protected $schema = [
  8. 'id' => 'int',
  9. 'name' => 'string',
  10. 'status' => 'int',
  11. 'score' => 'float',
  12. 'create_time' => 'datetime',
  13. 'update_time' => 'datetime',
  14. ];
  15. }

字段类型的定义可以使用PHP类型或者数据库的字段类型都可以,字段类型定义的作用主要用于查询的参数自动绑定类型。
时间字段尽量采用实际的数据库类型定义,便于时间查询的字段自动识别。如果是json类型直接定义为json即可。

未定义时优化

如果你没有定义schema属性的话,可以在部署完成后运行如下指令。

  1. php think optimize:schema

运行后会自动生成数据表的字段信息缓存。使用命令行缓存的优势是Db类的查询仍然有效。

字段类型(自动类型转换)

schema属性一旦定义,就必须定义完整的数据表字段类型。
支持给字段设置类型自动转换,会在写入和读取的时候自动进行类型转换处理,可以使用type属性,例如:

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class User extends Model
  5. {
  6. // 设置字段自动转换类型
  7. protected $type = [
  8. 'score' => 'float',
  9. ];
  10. }

type属性定义的不一定是实际的字段,也有可能是你的字段别名。

示例

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class User extends Model
  5. {
  6. protected $type = [
  7. 'status' => 'integer',
  8. 'score' => 'float',
  9. 'birthday' => 'datetime',
  10. 'info' => 'array',
  11. ];
  12. }

下面是一个类型自动转换的示例:

  1. $user = new User;
  2. $user->status = '1';
  3. $user->score = '90.50';
  4. $user->birthday = '2015/5/1';
  5. $user->info = ['a'=>1,'b'=>2];
  6. $user->save();
  7. var_dump($user->status); // int 1
  8. var_dump($user->score); // float 90.5;
  9. var_dump($user->birthday); // string '2015-05-01 00:00:00'
  10. var_dump($user->info);// array (size=2) 'a' => int 1 'b' => int 2

数据库查询默认取出来的数据都是字符串类型,如果需要转换为其他的类型,需要设置,支持的类型包括如下类型:

integer

设置为integer(整型)后,该字段写入和输出的时候都会自动转换为整型。

float

该字段的值写入和输出的时候自动转换为浮点型。

boolean

该字段的值写入和输出的时候自动转换为布尔型。

array

如果设置为强制转换为array类型,系统会自动把数组编码为json格式字符串写入数据库,取出来的时候会自动解码。

object

该字段的值在写入的时候会自动编码为json字符串,输出的时候会自动转换为stdclass对象。

serialize

指定为序列化类型的话,数据会自动序列化写入,并且在读取的时候自动反序列化。

json

指定为json类型的话,数据会自动json_encode写入,并且在读取的时候自动json_decode处理。

timestamp

指定为时间戳字段类型的话,该字段的值在写入时候会自动使用strtotime生成对应的时间戳,输出的时候会自动转换为dateFormat属性定义的时间字符串格式,默认的格式为Y-m-d H:i:s,如果希望改变其他格式,可以定义如下:

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class User extends Model
  5. {
  6. protected $dateFormat = 'Y/m/d';
  7. protected $type = [
  8. 'status' => 'integer',
  9. 'score' => 'float',
  10. 'birthday' => 'timestamp',
  11. ];
  12. }

或者在类型转换定义的时候使用:

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class User extends Model
  5. {
  6. protected $type = [
  7. 'status' => 'integer',
  8. 'score' => 'float',
  9. 'birthday' => 'timestamp:Y/m/d',
  10. ];
  11. }

然后就可以

  1. $user = User::find(1);
  2. echo $user->birthday; // 2015/5/1

datetime

timestamp类似,区别在于写入和读取数据的时候都会自动处理成时间字符串Y-m-d H:i:s的格式。

自动时间戳

系统支持自动写入创建和更新的时间戳字段(默认关闭),有两种方式配置支持。
自动时间戳类型默认为int,也就是在定义字段类型的时候定义为int或者不定义。

开启

第一种方式是全局开启,在数据库配置文件中进行设置:

  1. // 开启自动写入时间戳字段
  2. 'auto_timestamp' => true,

第二种是在需要的模型类里面单独开启:

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class User extends Model
  5. {
  6. protected $autoWriteTimestamp = true;
  7. }

又或者首先在数据库配置文件中全局开启,然后在个别不需要使用自动时间戳写入的模型类中单独关闭:

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class User extends Model
  5. {
  6. protected $autoWriteTimestamp = false;
  7. }

自定义类型,默认int,支持:timestamp/datetime/int

一旦配置开启的话,会自动写入create_timeupdate_time两个字段的值,默认为整型(int),如果你的时间字段不是int类型的话,可以直接使用:

  1. // 开启自动写入时间戳字段
  2. 'auto_timestamp' => 'datetime',

或者

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class User extends Model
  5. {
  6. protected $autoWriteTimestamp = 'datetime';
  7. }

默认的创建时间字段为create_time,更新时间字段为update_time,支持的字段类型包括timestamp/datetime/int
写入数据的时候,系统会自动写入create_timeupdate_time字段,而不需要定义修改器,例如:

  1. $user = new User();
  2. $user->name = 'thinkphp';
  3. $user->save();
  4. echo $user->create_time; // 输出类似 2016-10-12 14:20:10
  5. echo $user->update_time; // 输出类似 2016-10-12 14:20:10

时间字段的自动写入仅针对模型的写入方法,如果使用数据库的更新或者写入方法则无效。
时间字段输出的时候会自动进行格式转换,如果不希望自动格式化输出,可以把数据库配置文件的 datetime_format 参数值改为false
datetime_format参数支持设置为一个时间类名,这样便于你进行更多的时间处理,例如:

  1. // 设置时间字段的格式化类
  2. 'datetime_format' => '\org\util\DateTime',

该类应该包含一个__toString方法定义以确保能正常写入数据库。

自定义字段

如果你的数据表字段不是默认值的话,可以按照下面的方式定义:

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class User extends Model
  5. {
  6. // 定义时间戳字段名
  7. protected $createTime = 'create_at';
  8. protected $updateTime = 'update_at';
  9. }

下面是修改字段后的输出代码:

  1. $user = new User();
  2. $user->name = 'thinkphp';
  3. $user->save();
  4. echo $user->create_at; // 输出类似 2016-10-12 14:20:10
  5. echo $user->update_at; // 输出类似 2016-10-12 14:20:10

如果你只需要使用create_time字段而不需要自动写入update_time,则可以单独关闭某个字段,例如:

  1. namespace app\model;
  2. use think\Model;
  3. class User extends Model
  4. {
  5. // 关闭自动写入update_time字段
  6. protected $updateTime = false;
  7. }

支持动态关闭时间戳写入功能,例如你希望更新阅读数的时候不修改更新时间,可以使用isAutoWriteTimestamp方法:

  1. $user = User::find(1);
  2. $user->read +=1;
  3. $user->isAutoWriteTimestamp(false)->save();

只读字段

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

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class User extends Model
  5. {
  6. protected $readonly = ['name', 'email'];
  7. }

例如,上面定义了当前模型的nameemail字段为只读字段,不允许被更改。也就是说当执行更新方法之前会自动过滤掉只读字段的值,避免更新到数据库。
下面举个例子说明下:

  1. $user = User::find(5);
  2. // 更改某些字段的值
  3. $user->name = 'TOPThink';
  4. $user->email = 'Topthink@gmail.com';
  5. $user->address = '上海静安区';
  6. // 保存更改后的用户数据
  7. $user->save();

事实上,由于我们对nameemail字段设置了只读,因此只有address字段的值被更新了,而nameemail的值仍然还是更新之前的值。

动态只读字段

支持动态设置只读字段,例如:

  1. $user = User::find(5);
  2. // 更改某些字段的值
  3. $user->name = 'TOPThink';
  4. $user->email = 'Topthink@gmail.com';
  5. $user->address = '上海静安区';
  6. // 保存更改后的用户数据
  7. $user->readonly(['name','email'])->save();

只读字段仅针对模型的更新方法,如果使用数据库的更新方法则无效,例如下面的方式无效。

  1. $user = new User;
  2. // 要更改字段值
  3. $data['name'] = 'TOPThink';
  4. $data['email'] = 'Topthink@gmail.com';
  5. $data['address'] = '上海静安区';
  6. // 保存更改后的用户数据
  7. $user->where('id', 5)->update($data);

JSON字段

可以更为方便的操作模型的JSON数据字段。
这里指的JSON数据包括JSON类型以及JSON格式(但并不是JSON类型字段)的数据
我们修改下User模型类

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class User extends Model
  5. {
  6. // 设置json类型字段
  7. protected $json = ['info'];
  8. }

定义后,可以进行如下JSON数据操作。

定义JSON字段子字段类型

如果你需要查询的JSON属性是整型类型的话,可以在模型类里面定义JSON字段的属性类型,就会自动进行相应类型的参数绑定查询。

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class User extends Model
  5. {
  6. // 设置json类型字段
  7. protected $json = ['info'];
  8. // 设置JSON字段的类型
  9. protected $jsonType = [
  10. 'info->user_id' => 'int'
  11. ];
  12. }

没有定义类型的属性默认为字符串类型,因此字符串类型的属性可以无需定义。

写入JSON数据

使用数组方式写入JSON数据:

  1. $user = new User;
  2. $user->name = 'thinkphp';
  3. $user->info = [
  4. 'email' => 'thinkphp@qq.com',
  5. 'nickname '=> '流年',
  6. ];
  7. $user->save();

使用对象方式写入JSON数据

  1. $user = new User;
  2. $user->name = 'thinkphp';
  3. $info = new \StdClass();
  4. $info->email = 'thinkphp@qq.com';
  5. $info->nickname = '流年';
  6. $user->info = $info;
  7. $user->save();

查询JSON数据

  1. $user = User::find(1);
  2. echo $user->name; // thinkphp
  3. echo $user->info->email; // thinkphp@qq.com
  4. echo $user->info->nickname; // 流年

查询条件为JSON数据

  1. $user = User::where('info->nickname','流年')->find();
  2. echo $user->name; // thinkphp
  3. echo $user->info->email; // thinkphp@qq.com
  4. echo $user->info->nickname; // 流年

可以设置模型的JSON数据返回数组,只需要在模型设置jsonAssoc属性为true。

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class User extends Model
  5. {
  6. // 设置json类型字段
  7. protected $json = ['info'];
  8. // 设置JSON数据返回数组
  9. protected $jsonAssoc = true;
  10. }

设置后,查询代码调整为:

  1. $user = User::find(1);
  2. echo $user->name; // thinkphp
  3. echo $user->info['email']; // thinkphp@qq.com
  4. echo $user->info['nickname']; // 流年

更新JSON数据

  1. $user = User::find(1);
  2. $user->name = 'kancloud';
  3. $user->info->email = 'kancloud@qq.com';
  4. $user->info->nickname = 'kancloud';
  5. $user->save();

如果设置模型的JSON数据返回数组,那么更新操作需要调整如下。

  1. $user = User::find(1);
  2. $user->name = 'kancloud';
  3. $info['email'] = 'kancloud@qq.com';
  4. $info['nickname'] = 'kancloud';
  5. $user->info = $info;
  6. $user->save();

废弃字段

如果因为历史遗留问题 ,你的数据表存在很多的废弃字段,你可以在模型里面定义这些不再使用的字段。

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class User extends Model
  5. {
  6. // 设置废弃字段
  7. protected $disuse = [ 'status', 'type' ];
  8. }

在查询和写入的时候会忽略定义的statustype废弃字段

模型初始化

模型支持初始化,只需要定义init方法,例如:

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class User extends Model
  5. {
  6. // 模型初始化
  7. protected static function init()
  8. {
  9. //TODO:初始化内容
  10. }
  11. }

init必须是静态方法,并且只在第一次实例化的时候执行,并且只会执行一次。

模型操作

模型的操作方法不用和数据库查询一样调用必须首先调用table或者name方法,因为模型会按照规则自动匹配对应的数据表,例如:

  1. Db::name('user')->where('id','>',10)->select();

改成模型操作的话就变成

  1. User::where('id','>',10)->select();

虽然看起来是相同的查询条件,但一个最明显的区别是查询结果的类型不同。第一种方式的查询结果是一个(二维)数组,而第二种方式的查询结果是包含了模型(集合)的数据集。不过,在大多数情况下,这二种返回类型的使用方式并无明显区别。
模型操作和数据库操作的另外一个显著区别是模型支持包括获取器、修改器、自动时间写入在内的一系列自动化操作和事件,简化了数据的存取操作,但随之而来的是性能有所下降(其实并没下降,而是自动帮你处理了一些原本需要手动处理的操作),后面会逐步领略到模型的这些特色功能。

动态切换后缀

新版模型增加了一个数据表后缀属性,可以用于多语言或者数据分表的模型查询,省去为多个相同结构的表定义多个模型的麻烦。
默认的数据表后缀可以在模型类里面直接定义suffix属性。

  1. <?php
  2. namespace app\model;
  3. use think\Model;
  4. class Blog extends Model
  5. {
  6. // 定义默认的表后缀(默认查询中文数据)
  7. protected $suffix = _cn';
  8. }

你在模型里面定义的nametable属性无需包含后缀定义

模型提供了动态切换方法suffixsetSuffix,例如:

  1. // suffix方法用于静态查询
  2. $blog = Blog::suffix('_en')->find();
  3. $blog->name = 'test';
  4. $blog->save();
  5. // setSuffix用于动态设置
  6. $blog = new Blog($data);
  7. $blog->setSuffix('_en')->save();

模型方法依赖注入(暂未理解)

如果你需要对模型的方法支持依赖注入,可以把模型的方法改成闭包的方式,例如,你需要对获取器方法增加依赖注入

  1. public function getTestFieldAttr($value,$data) {
  2. return $this->invoke(function(Request $request) use($value,$data) {
  3. return $data['name'] . $request->action();
  4. });
  5. }

不仅仅是获取器方法,在任何需要依赖注入的方法都可以改造为调用invoke方法的方式,invoke方法第二个参数用于传入需要调用的(数组)参数。
如果你需要直接调用某个已经定义的模型方法(假设已经使用了依赖注入),可以使用

  1. protected function bar($name, Request $request) {
  2. // ...
  3. }
  4. protected function invokeCall(){
  5. return $this->invoke('bar',['think']);
  6. }

模型输出

模板输出

模型数据的模板输出可以直接把模型对象实例赋值给模板变量,在模板中可以直接输出,例如:

  1. <?php
  2. namespace app\controller;
  3. use app\model\User;
  4. use think\facade\View;
  5. class Index
  6. {
  7. public function index()
  8. {
  9. $user = User::find(1);
  10. View::assign('user', $user);
  11. return View::fetch();
  12. }
  13. }

在模板文件中可以使用

  1. {$user.name}
  2. {$user.email}

模板中的模型数据输出一样会调用获取器。

数组转换

可以使用toArray方法将当前的模型实例输出为数组,例如:

  1. $user = User::find(1);
  2. dump($user->toArray());

关联属性visiblehiddenappend方法

模型的visiblehiddenappend方法支持关联属性操作,例如:

| 方法 | 说明 | | —- | —- |

| visible | 参数为数组,仅输出指定字段 |

| hidden | 参数为数组,不输出的字段属性 |

| append | 参数为数组,追加输出 |

支持设置不输出的字段属性:

  1. $user = User::find(1);
  2. dump($user->hidden(['create_time','update_time'])->toArray());

数组输出的字段值会经过获取器的处理,也可以支持追加其它获取器定义(不在数据表字段列表中)的字段,例如:

  1. $user = User::find(1);
  2. dump($user->append(['status_text'])->toArray());

支持设置允许输出的属性,例如:

  1. $user = User::find(1);
  2. dump($user->visible(['id','name','email'])->toArray());

对于数据集结果一样可以直接使用(包括appendvisiblehidden方法)

  1. $list = User::select();
  2. $list = $list->toArray();

可以在查询之前定义hidden/visible/append方法,例如:

  1. dump(User::where('id',10)->hidden(['create_time','update_time'])->append(['status_text'])->find()->toArray());

注意,必须要首先调用一次Db类的方法后才能调用hidden/visible/append方法
模型的visiblehiddenappend方法支持关联属性操作,例如:

  1. $user = User::with('profile')->find(1);
  2. // 隐藏profile关联属性的email属性
  3. dump($user->hidden(['profile'=>['email']])->toArray());
  4. // 或者使用
  5. dump($user->hidden(['profile.email'])->toArray());
  6. hiddenvisibleappend方法同样支持数据集对象。

追加关联模型的属性到当前模型

支持追加关联模型的属性到当前模型,例如:

  1. $user = User::find(1);
  2. dump($user->append(['profile' => ['email', 'nickname']])->toArray());

profile是关联定义方法名,emailnicknameProfile模型的属性。