基本概念
模型会自动对应数据表,模型类的命名规则是除去表前缀的数据表名称,采用驼峰法命名,并且首字母大写,例如:
| 模型名称 | 约定对应数据表(假设数据库的前缀定义是 think_) | | —- | —- |
| User | think_user |
| UserType | think_user_type |
如果你的规则和上面的系统约定不符合,那么需要设置Model类的数据表名称属性,以确保能够找到对应的数据表。
模型自动对应的数据表名称都是遵循小写+下划线规范,如果你的表名有大写的情况,必须通过设置模型的table属性。
如果你希望给模型类添加后缀,需要设置name属性或者table属性。
<?phpnamespace app\model;use think\Model;class UserModel extends Model{protected $name = 'user';}
模型设置
默认主键为id,如果你没有使用id作为主键名,需要在模型中设置属性:
<?phpnamespace app\model;use think\Model;class User extends Model{protected $pk = 'uid';}
如果你想指定数据表甚至数据库连接的话,可以使用:
<?phpnamespace app\model;use think\Model;class User extends Model{// 设置当前模型对应的完整数据表名称protected $table = 'think_user';// 设置当前模型的数据库连接protected $connection = 'db_config';}
connection属性使用用配置参数名(需要在数据库配置文件中的connections参数中添加对应标识)。
常用的模型设置属性包括(以下属性都不是必须设置):
| 属性 | 描述 | | —- | —- |
| name | 模型名(相当于不带数据表前后缀的表名,默认为当前模型类名) |
| table | 数据表名(默认自动获取) |
| suffix | 数据表后缀(默认为空) |
| pk | 主键名(默认为id) |
| connection | 数据库连接(默认读取数据库配置) |
| query | 模型使用的查询类名称 |
| field | 模型允许写入的字段列表(数组) |
| schema | 模型对应数据表字段及类型 |
| type | 模型需要自动转换的字段及类型 |
| strict | 是否严格区分字段大小写(默认为true) |
| disuse | 数据表废弃字段(数组) |
| readonly | 只读字段(数组) |
| json | JSON字段(数组) |
模型不支持对数据表的前缀单独设置,并且也不推荐使用数据表的前缀设计,应该用不同的库区分。当你的数据表没有前缀的时候,name和table属性的定义是没有区别的,定义任何一个即可。
模型字段
模型的数据字段和对应数据表的字段是对应的,默认会自动获取(包括字段类型),但自动获取会导致增加一次查询,因此你可以在模型中明确定义字段信息避免多一次查询的开销。
<?phpnamespace app\model;use think\Model;class User extends Model{// 设置字段信息protected $schema = ['id' => 'int','name' => 'string','status' => 'int','score' => 'float','create_time' => 'datetime','update_time' => 'datetime',];}
字段类型的定义可以使用PHP类型或者数据库的字段类型都可以,字段类型定义的作用主要用于查询的参数自动绑定类型。
时间字段尽量采用实际的数据库类型定义,便于时间查询的字段自动识别。如果是json类型直接定义为json即可。
未定义时优化
如果你没有定义schema属性的话,可以在部署完成后运行如下指令。
php think optimize:schema
运行后会自动生成数据表的字段信息缓存。使用命令行缓存的优势是Db类的查询仍然有效。
字段类型(自动类型转换)
schema属性一旦定义,就必须定义完整的数据表字段类型。
支持给字段设置类型自动转换,会在写入和读取的时候自动进行类型转换处理,可以使用type属性,例如:
<?phpnamespace app\model;use think\Model;class User extends Model{// 设置字段自动转换类型protected $type = ['score' => 'float',];}
type属性定义的不一定是实际的字段,也有可能是你的字段别名。
示例
<?phpnamespace app\model;use think\Model;class User extends Model{protected $type = ['status' => 'integer','score' => 'float','birthday' => 'datetime','info' => 'array',];}
下面是一个类型自动转换的示例:
$user = new User;$user->status = '1';$user->score = '90.50';$user->birthday = '2015/5/1';$user->info = ['a'=>1,'b'=>2];$user->save();var_dump($user->status); // int 1var_dump($user->score); // float 90.5;var_dump($user->birthday); // string '2015-05-01 00:00:00'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,如果希望改变其他格式,可以定义如下:
<?phpnamespace app\model;use think\Model;class User extends Model{protected $dateFormat = 'Y/m/d';protected $type = ['status' => 'integer','score' => 'float','birthday' => 'timestamp',];}
或者在类型转换定义的时候使用:
<?phpnamespace app\model;use think\Model;class User extends Model{protected $type = ['status' => 'integer','score' => 'float','birthday' => 'timestamp:Y/m/d',];}
然后就可以
$user = User::find(1);echo $user->birthday; // 2015/5/1
datetime
和timestamp类似,区别在于写入和读取数据的时候都会自动处理成时间字符串Y-m-d H:i:s的格式。
自动时间戳
系统支持自动写入创建和更新的时间戳字段(默认关闭),有两种方式配置支持。
自动时间戳类型默认为int,也就是在定义字段类型的时候定义为int或者不定义。
开启
第一种方式是全局开启,在数据库配置文件中进行设置:
// 开启自动写入时间戳字段'auto_timestamp' => true,
第二种是在需要的模型类里面单独开启:
<?phpnamespace app\model;use think\Model;class User extends Model{protected $autoWriteTimestamp = true;}
又或者首先在数据库配置文件中全局开启,然后在个别不需要使用自动时间戳写入的模型类中单独关闭:
<?phpnamespace app\model;use think\Model;class User extends Model{protected $autoWriteTimestamp = false;}
自定义类型,默认int,支持:timestamp/datetime/int
一旦配置开启的话,会自动写入create_time和update_time两个字段的值,默认为整型(int),如果你的时间字段不是int类型的话,可以直接使用:
// 开启自动写入时间戳字段'auto_timestamp' => 'datetime',
或者
<?phpnamespace app\model;use think\Model;class User extends Model{protected $autoWriteTimestamp = 'datetime';}
默认的创建时间字段为create_time,更新时间字段为update_time,支持的字段类型包括timestamp/datetime/int。
写入数据的时候,系统会自动写入create_time和update_time字段,而不需要定义修改器,例如:
$user = new User();$user->name = 'thinkphp';$user->save();echo $user->create_time; // 输出类似 2016-10-12 14:20:10echo $user->update_time; // 输出类似 2016-10-12 14:20:10
时间字段的自动写入仅针对模型的写入方法,如果使用数据库的更新或者写入方法则无效。
时间字段输出的时候会自动进行格式转换,如果不希望自动格式化输出,可以把数据库配置文件的 datetime_format 参数值改为falsedatetime_format参数支持设置为一个时间类名,这样便于你进行更多的时间处理,例如:
// 设置时间字段的格式化类'datetime_format' => '\org\util\DateTime',
该类应该包含一个__toString方法定义以确保能正常写入数据库。
自定义字段
如果你的数据表字段不是默认值的话,可以按照下面的方式定义:
<?phpnamespace app\model;use think\Model;class User extends Model{// 定义时间戳字段名protected $createTime = 'create_at';protected $updateTime = 'update_at';}
下面是修改字段后的输出代码:
$user = new User();$user->name = 'thinkphp';$user->save();echo $user->create_at; // 输出类似 2016-10-12 14:20:10echo $user->update_at; // 输出类似 2016-10-12 14:20:10
如果你只需要使用create_time字段而不需要自动写入update_time,则可以单独关闭某个字段,例如:
namespace app\model;use think\Model;class User extends Model{// 关闭自动写入update_time字段protected $updateTime = false;}
支持动态关闭时间戳写入功能,例如你希望更新阅读数的时候不修改更新时间,可以使用isAutoWriteTimestamp方法:
$user = User::find(1);$user->read +=1;$user->isAutoWriteTimestamp(false)->save();
只读字段
用来保护某些特殊的字段值不被更改,这个字段的值一旦写入,就无法更改。 要使用只读字段的功能,我们只需要在模型中定义readonly属性
<?phpnamespace app\model;use think\Model;class User extends Model{protected $readonly = ['name', 'email'];}
例如,上面定义了当前模型的name和email字段为只读字段,不允许被更改。也就是说当执行更新方法之前会自动过滤掉只读字段的值,避免更新到数据库。
下面举个例子说明下:
$user = User::find(5);// 更改某些字段的值$user->name = 'TOPThink';$user->email = 'Topthink@gmail.com';$user->address = '上海静安区';// 保存更改后的用户数据$user->save();
事实上,由于我们对name和email字段设置了只读,因此只有address字段的值被更新了,而name和email的值仍然还是更新之前的值。
动态只读字段
支持动态设置只读字段,例如:
$user = User::find(5);// 更改某些字段的值$user->name = 'TOPThink';$user->email = 'Topthink@gmail.com';$user->address = '上海静安区';// 保存更改后的用户数据$user->readonly(['name','email'])->save();
只读字段仅针对模型的更新方法,如果使用数据库的更新方法则无效,例如下面的方式无效。
$user = new User;// 要更改字段值$data['name'] = 'TOPThink';$data['email'] = 'Topthink@gmail.com';$data['address'] = '上海静安区';// 保存更改后的用户数据$user->where('id', 5)->update($data);
JSON字段
可以更为方便的操作模型的JSON数据字段。
这里指的JSON数据包括JSON类型以及JSON格式(但并不是JSON类型字段)的数据
我们修改下User模型类
<?phpnamespace app\model;use think\Model;class User extends Model{// 设置json类型字段protected $json = ['info'];}
定义后,可以进行如下JSON数据操作。
定义JSON字段子字段类型
如果你需要查询的JSON属性是整型类型的话,可以在模型类里面定义JSON字段的属性类型,就会自动进行相应类型的参数绑定查询。
<?phpnamespace app\model;use think\Model;class User extends Model{// 设置json类型字段protected $json = ['info'];// 设置JSON字段的类型protected $jsonType = ['info->user_id' => 'int'];}
没有定义类型的属性默认为字符串类型,因此字符串类型的属性可以无需定义。
写入JSON数据
使用数组方式写入JSON数据:
$user = new User;$user->name = 'thinkphp';$user->info = ['email' => 'thinkphp@qq.com','nickname '=> '流年',];$user->save();
使用对象方式写入JSON数据
$user = new User;$user->name = 'thinkphp';$info = new \StdClass();$info->email = 'thinkphp@qq.com';$info->nickname = '流年';$user->info = $info;$user->save();
查询JSON数据
$user = User::find(1);echo $user->name; // thinkphpecho $user->info->email; // thinkphp@qq.comecho $user->info->nickname; // 流年
查询条件为JSON数据
$user = User::where('info->nickname','流年')->find();echo $user->name; // thinkphpecho $user->info->email; // thinkphp@qq.comecho $user->info->nickname; // 流年
可以设置模型的JSON数据返回数组,只需要在模型设置jsonAssoc属性为true。
<?phpnamespace app\model;use think\Model;class User extends Model{// 设置json类型字段protected $json = ['info'];// 设置JSON数据返回数组protected $jsonAssoc = true;}
设置后,查询代码调整为:
$user = User::find(1);echo $user->name; // thinkphpecho $user->info['email']; // thinkphp@qq.comecho $user->info['nickname']; // 流年
更新JSON数据
$user = User::find(1);$user->name = 'kancloud';$user->info->email = 'kancloud@qq.com';$user->info->nickname = 'kancloud';$user->save();
如果设置模型的JSON数据返回数组,那么更新操作需要调整如下。
$user = User::find(1);$user->name = 'kancloud';$info['email'] = 'kancloud@qq.com';$info['nickname'] = 'kancloud';$user->info = $info;$user->save();
废弃字段
如果因为历史遗留问题 ,你的数据表存在很多的废弃字段,你可以在模型里面定义这些不再使用的字段。
<?phpnamespace app\model;use think\Model;class User extends Model{// 设置废弃字段protected $disuse = [ 'status', 'type' ];}
在查询和写入的时候会忽略定义的status和type废弃字段
模型初始化
模型支持初始化,只需要定义init方法,例如:
<?phpnamespace app\model;use think\Model;class User extends Model{// 模型初始化protected static function init(){//TODO:初始化内容}}
init必须是静态方法,并且只在第一次实例化的时候执行,并且只会执行一次。
模型操作
模型的操作方法不用和数据库查询一样调用必须首先调用table或者name方法,因为模型会按照规则自动匹配对应的数据表,例如:
Db::name('user')->where('id','>',10)->select();
改成模型操作的话就变成
User::where('id','>',10)->select();
虽然看起来是相同的查询条件,但一个最明显的区别是查询结果的类型不同。第一种方式的查询结果是一个(二维)数组,而第二种方式的查询结果是包含了模型(集合)的数据集。不过,在大多数情况下,这二种返回类型的使用方式并无明显区别。
模型操作和数据库操作的另外一个显著区别是模型支持包括获取器、修改器、自动时间写入在内的一系列自动化操作和事件,简化了数据的存取操作,但随之而来的是性能有所下降(其实并没下降,而是自动帮你处理了一些原本需要手动处理的操作),后面会逐步领略到模型的这些特色功能。
动态切换后缀
新版模型增加了一个数据表后缀属性,可以用于多语言或者数据分表的模型查询,省去为多个相同结构的表定义多个模型的麻烦。
默认的数据表后缀可以在模型类里面直接定义suffix属性。
<?phpnamespace app\model;use think\Model;class Blog extends Model{// 定义默认的表后缀(默认查询中文数据)protected $suffix = _cn';}
你在模型里面定义的
name和table属性无需包含后缀定义
模型提供了动态切换方法suffix和setSuffix,例如:
// suffix方法用于静态查询$blog = Blog::suffix('_en')->find();$blog->name = 'test';$blog->save();// setSuffix用于动态设置$blog = new Blog($data);$blog->setSuffix('_en')->save();
模型方法依赖注入(暂未理解)
如果你需要对模型的方法支持依赖注入,可以把模型的方法改成闭包的方式,例如,你需要对获取器方法增加依赖注入
public function getTestFieldAttr($value,$data) {return $this->invoke(function(Request $request) use($value,$data) {return $data['name'] . $request->action();});}
不仅仅是获取器方法,在任何需要依赖注入的方法都可以改造为调用invoke方法的方式,invoke方法第二个参数用于传入需要调用的(数组)参数。
如果你需要直接调用某个已经定义的模型方法(假设已经使用了依赖注入),可以使用
protected function bar($name, Request $request) {// ...}protected function invokeCall(){return $this->invoke('bar',['think']);}
模型输出
模板输出
模型数据的模板输出可以直接把模型对象实例赋值给模板变量,在模板中可以直接输出,例如:
<?phpnamespace app\controller;use app\model\User;use think\facade\View;class Index{public function index(){$user = User::find(1);View::assign('user', $user);return View::fetch();}}
在模板文件中可以使用
{$user.name}{$user.email}
模板中的模型数据输出一样会调用获取器。
数组转换
可以使用toArray方法将当前的模型实例输出为数组,例如:
$user = User::find(1);dump($user->toArray());
关联属性visible、hidden和append方法
模型的visible、hidden和append方法支持关联属性操作,例如:
| 方法 | 说明 | | —- | —- |
|
visible
| 参数为数组,仅输出指定字段
|
|
hidden
| 参数为数组,不输出的字段属性
|
|
append
| 参数为数组,追加输出
|
支持设置不输出的字段属性:
$user = User::find(1);dump($user->hidden(['create_time','update_time'])->toArray());
数组输出的字段值会经过获取器的处理,也可以支持追加其它获取器定义(不在数据表字段列表中)的字段,例如:
$user = User::find(1);dump($user->append(['status_text'])->toArray());
支持设置允许输出的属性,例如:
$user = User::find(1);dump($user->visible(['id','name','email'])->toArray());
对于数据集结果一样可以直接使用(包括append、visible和hidden方法)
$list = User::select();$list = $list->toArray();
可以在查询之前定义hidden/visible/append方法,例如:
dump(User::where('id',10)->hidden(['create_time','update_time'])->append(['status_text'])->find()->toArray());
注意,必须要首先调用一次Db类的方法后才能调用hidden/visible/append方法
模型的visible、hidden和append方法支持关联属性操作,例如:
$user = User::with('profile')->find(1);// 隐藏profile关联属性的email属性dump($user->hidden(['profile'=>['email']])->toArray());// 或者使用dump($user->hidden(['profile.email'])->toArray());hidden、visible和append方法同样支持数据集对象。
追加关联模型的属性到当前模型
支持追加关联模型的属性到当前模型,例如:
$user = User::find(1);dump($user->append(['profile' => ['email', 'nickname']])->toArray());
profile是关联定义方法名,email和nickname是Profile模型的属性。
