thinkphp-模型的获取器和修改器、模型的查询范围、搜索器

7367次阅读 176人点赞 作者: WuBin 发布时间: 2024-04-19 10:52:14
扫码到手机查看

模型的获取器和修改器

获取器

获取器的作用是对模型实例的数据做出自动处理,一个获取器对应模型的一个特殊方法,该方法为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字段进行处理,并将其每一行的值改为大写

点赞 支持一下 觉得不错?客官您就稍微鼓励一下吧!
关键词:thinkphp
推荐阅读
  • uniapp实现被浏览器唤起的功能

    当用户打开h5链接时候,点击打开app若用户在已经安装过app的情况下直接打开app,若未安装过跳到应用市场下载安装这个功能在实现上主要分为两种场景,从普通浏览器唤醒以及从微信唤醒。

    9664次阅读 631人点赞 发布时间: 2022-12-14 16:34:53 立即查看
  • Vue

    盘点Vue2和Vue3的10种组件通信方式

    Vue中组件通信方式有很多,其中Vue2和Vue3实现起来也会有很多差异;本文将通过选项式API组合式API以及setup三种不同实现方式全面介绍Vue2和Vue3的组件通信方式。

    4342次阅读 322人点赞 发布时间: 2022-08-19 09:40:16 立即查看
  • JS

    几个高级前端常用的API

    推荐4个前端开发中常用的高端API,分别是MutationObserver、IntersectionObserver、getComputedstyle、getBoundingClientRect、requ...

    14542次阅读 951人点赞 发布时间: 2021-11-11 09:39:54 立即查看
  • PHP

    【正则】一些常用的正则表达式总结

    在日常开发中,正则表达式是非常有用的,正则表达式在每个语言中都是可以使用的,他就跟JSON一样,是通用的。了解一些常用的正则表达式,能大大提高你的工作效率。

    13594次阅读 496人点赞 发布时间: 2021-10-09 15:58:58 立即查看
  • 【中文】免费可商用字体下载与考证

    65款免费、可商用、无任何限制中文字体打包下载,这些字体都是经过长期验证,经得住市场考验的,让您规避被无良厂商起诉的风险。

    12136次阅读 971人点赞 发布时间: 2021-07-05 15:28:45 立即查看
  • Vue

    Vue3开发一个v-loading的自定义指令

    在vue3中实现一个自定义的指令,有助于我们简化开发,简化复用,通过一个指令的调用即可实现一些可高度复用的交互。

    16475次阅读 1315人点赞 发布时间: 2021-07-02 15:58:35 立即查看
  • JS

    关于手机上滚动穿透问题的解决

    当页面出现浮层的时候,滑动浮层的内容,正常情况下预期应该是浮层下边的内容不会滚动;然而事实并非如此。在PC上使用css即可解决,但是在手机端,情况就变的比较复杂,就需要禁止触摸事件才可以。

    15243次阅读 1240人点赞 发布时间: 2021-05-31 09:25:50 立即查看
  • Vue

    Vue+html2canvas截图空白的问题

    在使用vue做信网单页专题时,有海报生成的功能,这里推荐2个插件:一个是html2canvas,构造好DOM然后转canvas进行截图;另外使用vue-canvas-poster(这个截止到2021年3月...

    30010次阅读 2359人点赞 发布时间: 2021-03-02 09:04:51 立即查看
  • Vue

    vue-router4过度动画无效解决方案

    在初次使用vue3+vue-router4时候,先后遇到了过度动画transition进入和退出分别无效的情况,搜遍百度没没找到合适解决方法,包括vue-route4有一些API都进行了变化,以前的一些操...

    26052次阅读 2003人点赞 发布时间: 2021-02-23 13:37:20 立即查看
交流 收藏 目录