命名规范
数据表命名
数据表和字段采用小写加下划线方式命名,例如think_user表和user_name字段,禁止使用驼峰、中文或者拼音作为数据表及字段命名。
字段规范
主键统一使用id;
外键统一使用resource_id形式(例如user_id);
模型数据字段统一使用小写+下划线命名,和数据表字段规范一致;
数据表统一添加系统时间字段(create_time和update_time),并使用datetime类型;
使用软删除并添加时间字段delete_time,类型和系统时间字段保持一致;
模型类应该继承一个统一的公共类,便于调整和统一设置;
模型类应当通过定义autoWriteTimestamp属性明确时间字段类型;
查询规范
不要在数据库配置文件以外的地方配置或者动态设置数据库连接信息,包括模型内部。
尽量不使用原生SQL查询,而应当使用查询构造器。
不要使用任何数据库工具创建、修改数据表和填充数据,应当使用数据迁移并同步版本库给所有成员。
每次数据查询都用Db类或者模型类的静态方法。
避免在模型方法中直接写复杂的查询条件,而应当使用查询范围或者搜索器统一定义后调用。用查询表达式方式替代传统的数组查询。
查询数据的处理统一使用获取器定义,而不要直接处理数据。
对写入数据需要额外处理的话统一使用修改器。
对于使用了SQL函数的用法,使用fieldRaw、orderRaw和whereRaw/whereExp替代field、order和where用法。
仅在使用字符串查询条件,以及调用whereExp和whereRaw方法的时候需要使用手动参数绑定,其余情况下都会自动进行参数绑定,禁止手动调用bind方法。
不要在WEB访问的时候进行大量数据操作,容易超时的数据处理应当在命令行下通过创建指令完成。
查询值为Null的数据
查询值为Null的数据应当使用whereNull或者whereNotNull方法
// 查询email为空,并且name不为空的用户数据
User::whereNull('email')
->whereNotNull('name')
->select();
使用快捷方法
对于一些常用的查询,尽量使用系统封装的快捷查询方法,例如:
User::whereIn('id', [1,2,3])
->whereLike('name', 'think%')
->select();
相当于下面的查询
User::where('id', 'in', [1,2,3])
->where('name', 'like', 'think%')
->select();
更多的方法可以参考官方手册或者使用IDE的自动提示。
获取字段值和列数据
对于一些简单的数据获取,你完全不需要查询整个表的数据,例如查询某个字段(满足条件的)值或者列数据。
// 获取id为10的用户名称
User::where('id', 10)
->value('name');
// 获取状态为1的用户名称列表
User::where('status', 1)
->column('name');
// 获取分数大于80的用户分数列表,以用户ID为索引
User::where('score', '>', 80)
->column('score', 'id');
聚合查询
如果你的min/max查询的是一个字符串类型字段,记得加上第二个参数并传入false。
// 获取name字段的最大值
User::max('name', false);
时间区间查询
时间查询主要用于时间字段的区间查询,whereTime方法的优势是支持自动识别时间字段类型并进行转换处理。
// 大于某个时间
User::whereTime('birthday', '>=', '2008-10-1')
->select();
// 小于某个时间
User::whereTime('birthday', '<', '2000-10-1')
->select();
// 时间区间查询
User::whereBetweenTime('birthday', '1990-10-1', '2000-10-1')
->select();
// 不在某个时间区间
User::whereNotBetweenTime('birthday', '1970-10-1', '2000-10-1')
->select();
年/月/日/周查询
对于年/月/日/周的时间查询,推荐使用whereYear/whereMonth/whereDay/whereWeek方法查询,例如:
// 查询本月注册的用户
Db::name('user')
->whereMonth('create_time')
->select();
// 查询上月注册用户
Db::name('user')
->whereMonth('create_time','last month')
->select();
// 查询2018年6月注册的用户
Db::name('user')
->whereMonth('create_time', '2018-06')
->select();
// 查询当天注册的用户
Db::name('user')
->whereDay('create_time')
->select();
// 查询昨天注册的用户
Db::name('user')
->whereDay('create_time', 'yesterday')
->select();
// 查询2018年6月1日注册的用户
Db::name('user')
->whereDay('create_time', '2018-06-01')
->select();
时间表达式查询
高级的时间表达式查询可以使用PHP的相对时间格式,例如:
// 查询两天以内的博客
Blog::whereTime('create_time','-2 days')
->select();
// 查询昨天中午后发的博客
Blog::whereTime('create_time','yesterday noon')
->select();
更多的时间表达式查询你可以自由发挥。
时间字段范围查询
你可以查询当前时间是否在两个时间字段区间范围内,通常用于一些活动以及优惠券的有效期查询等等。
// 查询有效期内的活动
Event::whereBetweenTimeField('start_time','end_time')
->select();
// 查询没有开始或者已经过期的活动
Event::whereNotBetweenTimeField('start_time','end_time')
->select();
字段比较
可以直接比较两个字段的大小进行查询
User::whereColumn('update_time', '>', 'create_time')
->select();
User::whereColumn('score1', '>', 'score2')
->select();
如果需要比较两个字段相同,可以使用
User::whereColumn('score1', 'score2')
->select();
条件查询
应当使用条件查询替代在组装查询条件的时候写大量的if和else。
User::when($condition, function ($query) {
// 满足条件后执行
$query->where('score', '>', 80)->limit(10);
})->select();
并且支持不满足条件的分支查询,并且支持多次调用when方法。
User::when($condition, function ($query) {
// 满足条件后执行
$query->where('score', '>', 80)->limit(10);
}, function ($query) {
// 不满足条件执行
$query->where('score', '>', 60);
})->select();
JSON查询
如果你的字段类型使用的是JSON类型,那么可以直接使用框架提供的JSON查询支持。
User::where('info->nickname', 'ThinkPHP')
->find();
注意,需要在模型里面定义JSON字段属性。
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
// 设置json类型字段
protected $json = ['info'];
}
如果使用Db查询的话,可以改为
$user = Db::name('user')
->json(['info'])
->where('info->nickname','ThinkPHP')
->find();
SQL函数查询
如果需要对某个字段使用SQL函数表达式查询,可以使用
User::whereExp('nickname', "= CONCAT(name, '-', id)")
->whereRaw('LEFT(nickname, 5) = ?', ['think'])
->select();
注意whereExp和whereRaw方法的区别,前者是对某个字段使用SQL函数表达式,后者是整个查询就是一个SQL函数表达式。
字段递增/递减
可以使用:
// 博客的阅读数递增1 评论数递减2
Blog::where('id', 10)
->inc('read_count')
->dec('comment_count', 2)
->save();
新版已经取消了延时更新功能。
指定字段值排序
如果你需要按照指定字段的值的顺序来排序,可以使用
User::where('status', 1)
->orderField('id', [1,2,3])
->select();
从主库读取
如果你使用了数据库的主从分离,当刚写入数据后,数据库的主从同步可能还没来得及同步,这个时候立刻查询数据可能会出错,你可以使用下面的方法从主库读取。
$user = User::create($data);
$user->readMaster()->select();
你可以全局配置数据写入后自动读取主库
// 模型写入后自动读取主服务器
'read_master' => true,
获取自增ID
使用Db类的insert方法或者模型的save方法返回的不是自增主键,不过你可以使用。
$userId = Db::name('user')->insertGetId($data);
如果使用模型的话,自增主键的值会自动赋值给模型对象,可以直接获取。
$user = User::create($data);
echo $user->id;
模型查询为空的处理
模型查询数据不存在的话返回值为Null,所以必须要添加返回值判断然后进行数据处理,建议使用下面的方法查询,如果数据不存在则返回空的模型对象。
// 始终返回模型对象
$user = User::where('id', 10)->findOrEmpty();
自动分批写入
如果你需要一次写入大量数据,建议使用limit方法自动分批多次写入。
// 自动分批多次写入数据库 每次最多写入1000条
Db::name('user')
->limit(1000)
->insertAll($dataList);
如果是使用模型的话,建议直接使用saveAll方法而不需要limit方法。
$user = new User;
$user->saveAll($dataList);
数据分批处理
对于大量数据的处理操作,建议使用chunk分批处理方法。
// 每次处理100个数据
User::chunk(100, function($users) {
foreach ($users as $user) {
// 处理数据
}
});
游标查询
对于内存开销比较大的应用,在做大量数据查询和处理的时候,建议使用cursor方法进行游标查询,可以利用PHP的生成器特性,减少内存占用。
$cursor = User::cursor();
foreach($cursor as $user){
// 处理数据
}
关联查询
关联方法定义应该始终使用小驼峰规范,但关联查询的时候支持驼峰或者小写加下划线方法,但区别在于关联属性的名称不同。
发表评论