thinkphp-模型的获取器和修改器、模型的查询范围、搜索器
模型的获取器和修改器
获取器
获取器的作用是对模型实例的数据做出自动处理,一个获取器对应模型的一个特殊方法,该方法为public,方法名的命名规范为:getFieldAttr();即为get字段名Attr()。
比如我有一些代表用户状态的字段status,在数据库中用数值表示,-1,0,1,2,正常情况,我们会在获取后,用php做对应关系:
$user = UserModel::find(19);
return $user->status; // -1
如果用了获取器,我们就可以在获取阶段,直接对数值做对应关系:
控制器中(直接输出数据库字段的值即可得到获取器转换的对应值):
public function getAttr()
{
$user = UserModel::find(19);
// echo 改为return的话 model中的就不会执行
echo $user->status;
}
模型中
// 设置status修改器
public function getStatusAttr($value)
{
// 这个value值就是在控制器中执行$user->status,那么这个方法就会被激活
// 而这个$user->status返回值就会被传递到$value
echo 'model中打印' . $value . '<br>';
$arr = [-1=>'删除', 0=>'禁用', 1=>'正常', 2=>'待审核'];
return $arr[$value];
}
模型中的修改器还支持第二个参数
// 设置status修改器
public function getStatusAttr($value, $data)
{
// 第二个参数$data得到的是整条的数据
dump($data);
}
除了getFieldAttr 中Field 可以是字段值,也可以是自定义的虚拟字段
模型中(Nothing 这个字段不存在,而此时参数$value 只是为了占位,并未使用,第二个参数$data 得到的是筛选到的整行的数据,然后得到最终值;)
// 设置一个虚拟nothing字段的修改器
public function getNothingAttr($value, $data)
{
$arr = [-1=>'删除', 0=>'禁用', 1=>'正常', 2=>'待审核'];
return $arr[ $data['status'] ];
}
在控制器中
public function getAttr()
{
$user = UserModel::find(19);
// 返回自定义nothing字段的内容
echo $user->nothing;
}
如果你定义了获取器,并且想获取原始值,可以使用getData()方法
控制器中
public function getAttr()
{
$user = UserModel::find(19);
echo $user->nothing;
// getData(‘要获取的字段名’)
echo $user->getData('status');
}
如果使用了修改器,那么在最终输出值的时候,就会按照修改器中修改后的值输出,比如
模型中
// 设置status修改器
public function getStatusAttr($value, $data)
{
$arr = [-1=>'删除', 0=>'禁用', 1=>'正常', 2=>'待审核'];
return $arr[$value];
}
控制器
public function getAttr()
{
$user = UserModel::find(19);
return json($user);
}
最终输出如下:
{
id: 19,
username: "蜡笔小新",
password: "123",
gender: "男",
status: "删除",
...
}
而这时,如果要跳过修改器,直接输出原数据,只需要在控制器中,做如下修改:
$user = UserModel::find(19);
return json($user->getData());
使用WithAttr 在控制器端实现动态获取器,比如设置所有email 为大写
// 在tp6.1.4中运行报错,此处仅作了解
$user = UserModel::WithAttr('email', function($value) {
return strtoupper($value);
})->find(19);
$user = UserModel::WithAttr('status', function ($value) {
$status = [-1=>'删除', 0=>'禁用', 1=>'正常', 2=>'待审核'];
return $status[$value];
})->select();
同时定义了模型获取器和动态获取器,那么动态获取器优先级更高;
修改器
模型修改器的作用,就是对模型设置对象的值进行处理,比如,我们要新增数据的时候,对数据就行格式化、过滤、转换等处理。
模型修改器的命名规则为:setFieldAttr;field=字段名
我们要设置一个新增,规定邮箱的英文都必须大写,修改器如下:
模型中
// email修改器
public function setEmailAttr($value)
{
return strtoupper($value);
}
控制器中
public function insert() {
$user = new UserModel();
$user = UserModel::create(
//参数1 是新增数据数组,必选
[
'username' => '李白3',
'password' => '123',
'gender' => '男',
'email' => 'libai@163.com',
'price' => 100,
'details' => '123',
'uid' => 1011
],
//参数2 是允许写入的字段 allowField,可选,默认为[]
['username', 'password', 'details', 'email'],
// 参数3 为是否replace 新增,默认false 为Insert 写入,改为true则为replace新增,
false
);
dump($user->email); // $user->id
}
最终输出:^"LIBAI@163.COM" 即:在插入或者修改该email字段的时候,会自动将赋予这个字段的值变为大写。
模型的查询范围
模型的查询范围是在模型端创建一个封装的查询或写入方法(封装最常用的sql查询),方便控制器端等调用;比如,封装一个筛选所有性别为男的查询,并且只显示部分字段5 条;
在模型中:
// 查询范围 男 5条
public function scopeMale($query)
{
$query->where('gender', '男')
->field('id, username, gender,email')
->limit(5);
}
在控制器中
$result = UserModel::scope('male')->select();
return json($result);
同时其也支持动态方法:
$result = UserModel::male()->select();
查询封装可以传递参数(给模型中的方法传递第二个参数),比如,通过邮箱查找某人
模型中:
public function scopeEmail($query, $value)
{
$query->where('email', 'like', '%' . $value . '%');
}
控制器中
$result = UserModel::scope('email', 'xiao')->select();
如果使用动态方法的话,只需要传入一个参数即可
$result = UserModel::email('xiao')->select();
也可以实现多个查询封装方法连缀调用,比如找出邮箱xiao 并大于80 分的;
模型中
public function scopeEmail($query, $value)
{
$query->where('email', 'like', '%' . $value . '%');
}
public function scopePrice($query, $value)
{
$query->where('price', '>', 80);
}
控制器中
$result = UserModel::scope('email', 'xiao')
->scope('price', 80)
->select();
或者使用动态方法:
$result = UserModel::email('xiao')->price(80)->select();
查询范围只能使用find()和select()两种方法;
全局范围查询,就是在此模型下不管怎么查询都会加上全局条件
比如,在模型中进行如下设置:
// 强制全局查询条件,任何查询都必须加上这个条件
// 定义全局
protected $globalScope = ['status']; // 数组中可以传入多个值
// 定义全局方法
public function scopeStatus($query)
{
$query->where('status', 1);
}
模型中
UserModel::select();
return Db::getLastSql();
这时候,可以看到最终的查询语句为:SELECT * FROM `tp_user` WHERE `status` = 1
注意:$globalScope中设置的字段名与下方要使用的方法scopeStatus必须要一一对应!!
同理,我们可以再看一下前面的例子
$result = UserModel::scope('email', 'xiao')
->scope('price', 80)
->select();
return Db::getLastSql();
最终输出的SQL语句:SELECT * FROM `tp_user` WHERE `status` = 1 AND `email` LIKE '%xiao%' AND `price` > '80'
就是无论怎么查询,前面一定会加上一个必须status=1
在定义了全局查询后,如果想取消这个查询的所有全局查询,可以用下面方法
如果模型中的全局定义了多个强制字段,而使用的时候要取消所有的强制字段:
UserModel::withoutGlobalScope()->select();
return Db::getLastSql();
那么这时的查询语句就是:SELECT * FROM `tp_user` 取消了各种限制。
如果要取消其中某个强制,那么只需要传入对应的参数即可:
UserModel::withoutGlobalScope(['status'])->select();
模型搜索器
搜索器是用于封装字段(或搜索标识)的查询表达式,类似查询范围,一个搜索器对应模型的一个特殊方法,该方法为public,方法名的命名规范为:searchFieldAttr()
举个例子,我们要封装一个邮箱字符模糊查询,然后封装一个时间限定查询
模型中
public function searchEmailAttr($query, $value, $data)
{
dump($value);
dump($data);
// $query->where('email', 'like', '%' . $value . '%');
}
控制器中
UserModel::withSearch(
['email'],
['email' => 'xiao']
);
其中,第一个参数中的email指的是模型中的Email方法,第二个参数的email指的是数据表中的email字段,并给该字段传递值,对应模型中方法的第二个参数$value
最终输出结果:
$value ===》 "xiao"
$data ====》 array:1 [
"email" => "xiao"
]
可见$value就是传递的值,而$data则是整个数组。
withSearch()中第一个数组参数,限定搜索器的字段,第二个则是表达式值
正常获取数据:
$result = UserModel::withSearch(
['email'],
['email' => 'xiao']
)->select();
注意:search也受 protected $globalScope = ['xxx']; 的影响
模型中
// 搜索器
public function searchEmailAttr($query, $value, $data)
{
$query->where('email', 'like', '%' . $value . '%');
}
public function searchCreateTimeAttr($query, $value, $data) {
// 这里传递来的$value可能是一个数组了 即为起始时间和结束时间
$query->whereBetweenTime('create_time', $value[0], $value[1]);
}
控制器中
$result = UserModel::withSearch(
['email', 'create_time'],
[
'email' => 'xiao',
'create_time' => [
'2014-01-01',
'2017-01-01'
]
]
)->select();
return Db::getLastSql();
最终返回的sql语句:SELECT * FROM `tp_user` WHERE `email` LIKE '%xiao%' AND `create_time` BETWEEN '2014-01-01 00:00:00' AND '2017-01-01 14:00:00'
所以第一个参数数组中的字段,决定了要使用模型中的哪些方法,而第二个参数数组则是传入该方法的对应的$value的参数。
而且,如果删除第一个数组中的参数,而保留第二个数组参数中的传参,以第一个参数方法为准。
$result = UserModel::withSearch(
['email'],
[
'email' => 'xiao',
'create_time' => [
'2014-01-01',
'2017-01-01s'
]
]
)->select();
return Db::getLastSql();
最终SQL语句:SELECT * FROM `tp_user` WHERE `email` LIKE '%xiao%',比scope要灵活一些。
如果想在搜索器查询的基础上再增加查询条件,直接使用链式查询即可;
$result = UserModel::withSearch(
['email'],
[
'email' => 'xiao',
'create_time' => [
'2014-01-01',
'2017-01-01'
]
]
)->where('gender', '男')->select();
如果想在搜索器查询的基础上再增加查询条件,直接使用链式查询即可;
$result = UserModel::withSearch(
['email', 'create_time'],
[
'email' => 'xiao',
'create_time' => [
'2014-01-01',
'2017-01-01'
]
]
)->where('gender', '男')
->order('price', 'desc')
->select();
同时也提供了另外的一种方法,我们首先在控制器中withSearch的第二个参数数组中,定义一个自定义的方法sort:
$result = UserModel::withSearch(
['email', 'create_time'],
[
'email' => 'xiao',
'create_time' => [
'2014-01-01',
'2017-01-01'
],
'sort' => ['price' => 'desc']
]
)->where('gender', '男')
->select();
return Db::getLastSql();
然后在控制器中的一个被调用的方法中
public function searchEmailAttr($query, $value, $data)
{
$query->where('email', 'like', '%' . $value . '%');
if(isset($data['sort'])) {
$query->order($data['sort']);
}
}
搜索器的第三个参数$data,可以得到withSearch()方法第二参数的值,这里由于$data中获取的是
$data = [
'email' => 'xiao',
'create_time' => [
'2014-01-01',
'2017-01-01'
],
'sort' => ['price' => 'desc']
]
所以我们可以先判断$data['sort]在不在,在的话直接将其值传入order中。最终sql为:
SELECT * FROM `tp_user` WHERE `email` LIKE '%xiao%' AND `create_time` BETWEEN '2014-01-01 00:00:00' AND '2017-01-01 00:00:00' AND `gender` = '男' ORDER BY `price` DESC
方法的别名
如果觉得比如create_time太长,想使用别名,可以这么设置(注意,别名修改要统一对应,上面改了下面也要改!)
$result = UserModel::withSearch(
['email', 'create_time' => 'ctime'],
[
'email' => 'xiao',
'ctime' => [
'2014-01-01',
'2017-01-01'
],
'sort' => ['price' => 'desc']
]
)->where('gender', '男')
->select();
模型数据集
数据集也是直接继承collection 类,所以和数据库方式一样,数据集也是直接继承collection 类,所以和数据库方式一样
数据集也是直接继承collection 类,所以和数据库方式一样
$resut = UserModel::where('id', 111)->select();
if ($resut->isEmpty()) {
return '没有数据!';
}
更多数据集方法,直接参考数据库那篇的表格即可
使用模型方法hidden()可以隐藏某个字段,使用visible()只显示某个字段
$result = UserModel::withSearch(
['email', 'create_time' => 'ctime'],
[
'email' => 'xiao',
'ctime' => [
'2014-01-01',
'2017-01-01'
],
'sort' => ['price' => 'desc']
]
)->where('gender', '男')
->select();
return json($result->hidden([
'username', 'detail'
]));
上面例子就是将结果中的username和detail字段的数值内容隐藏掉。
也可以使用append给结果集中加入某个字段,其效果等同于前面讲过的自定义的模型修改器(获取器)一文中的getNothingAttr方法
return json(
$result ->hidden([
'username', 'detail'
]) ->append(
['noothing']
)
);
visible是只能显示指定的字段
return json(
$result ->hidden([
'username', 'detail' // 结果中隐藏掉设置的字段
])->visible([
'email', 'username' // 结果集中只能显示这两个字段
])->append(
['noothing'] // 向结果集中添加新属性noothing,其对应值为null
)
);
使用append()可以添加某个获取器字段,使用withAttr()对字段进行函数处理;
$result ->hidden([
'username', 'detail' // 结果中隐藏掉设置的字段
])->visible([
'email', 'username' // 结果集中只能显示这两个字段
])->append(
['noothing'] // 向结果集中添加新属性noothing,其对应值为null
)->withAttr('email', function($value) {
return strtoupper($value);
})
对取出来的结果集中的email字段进行处理,并将其每一行的值改为大写