ThinkPHP:模型三大利器之三(获取器)

发布时间 - 2019-12-16 00:00:00    点击率:

定义获取器

获取器的作用是对模型对象的(原始)数据做出自动处理。一个获取器对应模型的一个特殊方法(该方法必须为public类型),方法命名规范为:

getFieldNameAttr

FieldName为数据表字段的驼峰转换或者你数据表不存在的字段(注意理解后面这句话),下面是一个典型的获取器定义:

 '普通', 1 => 'VIP', 2 => '黄金', 3 => '白金', 4 => '钻石'];
        return $type[$value];
    }
}

你需要给每一个需要输出转换处理的数据字段定义一个对应的获取器,但获取器的字段名不一定要和数据表的字段名一致,例如我希望给user_type字段定义一个名为getTypeAttr的获取器也是允许的,但要注意这个时候传入获取器的第一个参数肯定是没有值(因为没有对应的数据表字段数据),只能通过第二个参数获取你需要的数据。

 '普通', 1 => 'VIP', 2 => '黄金', 3 => '白金', 4 => '钻石'];
        return $type[$data['user_type']];
    }
}

当然更为严谨的情况下,你还需要判断下是否存在$data['user_type'],这个暂且略过。

注意第二个参数的data数据,可能本身已经经过了获取器的处理(如果你定义了相关的获取器的话)。

为什么要定义一个和数据报字段不一致的获取器呢?最明显的好处可以区分不同的字段获取原始数据和处理过的数据。事实上,有很多理由可以让你定义一些数据表不存在的字段获取器,这恰恰是获取器的魅力所在。

看的出来获取器定义本身没什么难度,关键在于方法里面的获取逻辑,这是实际应用中最需要关注的。

调用获取器

定义获取器之后会在下列情况自动触发:

·模型的数据对象取值操作(例如$model->field_name);

·模型的序列化输出操作(例如$model->toArray()或toJson());

·显式调用getAttr方法(例如$model->getAttr('field_name'));

前面两种其实最终都是调用最后一种来实现的,最关键的是要理解第一种。模型对象取值的时候一般都是通过下面的方式:

$user = User::get(1);
echo $user->name;
echo $user->user_type;

当我们使用上面的方式进行模型对象数据获取或者在模板输出的时候事实上都会按照下面的顺序来检测和获取数据。

·第1步——如果查询结果包含该字段数据,取回原始数据,否则并进入第2步;

·第2步——检查是否定义该字段的获取器(包括动态获取器),如果有,则调用获取器返回结果,没有则进入第3步;

·第3步——检查是否定义了字段的类型转换,有则进行转换处理并返回结果,没有则进入第4步;

·第4步——如果是系统的时间字段,则自动进行时间格式化处理并返回结果,否则进入第5步;

·第5步——如果第1步检查的时候不包含该字段数据,则检查是否存在关联属性定义,有则通过关联关系获取数据并返回结果,否则抛出属性未定义的异常。

上面的这五个步骤的详细代码,如果你有兴趣的可以直接参考think\model\concern\Attribute的getAttr方法代码。

简单来说,当你获取$user->user_type的时候都会去检查是否定义了相关的获取器,而不管user_type字段是否是一个真实的数据表字段。

但很多情况下,你不会一个个去获取模型数据,而是把整个模型数据返回给客户端或者模板。

public function index()
{
    $user = User::get(1);
    return json($user);
}

在这种情况下,其实就是在响应输出的时候进行了模型的toJson处理。

有一点至关重要,如果你的获取器定义了非数据表的字段,是不会自动输出的,必须通过append方法追加额外属性(并且支持追加关联模型属性)。

如果我们定义了一个type属性的获取器(假设这并不是一个真实的数据表字段),那么需要使用下的方式才能正常输出(否则你可能只有user_type数据):

public function index()
{
    $user = User::get(1);
    return json($user->append(['type']));
}

如果你是使用toArray的话,处理方式相同。

如果是数据集查询的话,一样可以使用append方法统一追加额外字段。

public function index()
{
    $users = User::all();
    return json($users->append(['type']));
}

除了append方法之外,我们还支持用hidden方法临时隐藏一些数据。

获取原始数据

有些情况下,除了要获取处理过的数据外,还需要获取原始数据以便应对不同的需求。

如果你的获取器都是用的区分于实际数据表字段的额外属性字段,那么这个问题本身已经解决了。所以我们主要讨论的是当你的获取器属性和数据表字段一致的情况下,该如何获取原始数据。

一个最简单的办法是使用getData方法:

$user = User::get(1);
// 获取user_type获取器数据
echo $user->user_type;
// 获取原始的user_type数据
echo $user->getData('user_type');
// 获取全部原始数据
dump($user->getData());

动态获取器

前面我们提到过动态获取器的概念,动态获取器就是不需要在模型类里面定义获取器方法,而是在查询的时候使用闭包来定义一个字段的获取器对数据进行统一的处理。

User::withAttr('name', function($value, $data) {
return strtolower($value);
})->select();

如果你需要定义多个动态获取器,多次调用withAttr方法就行。

动态获取器的意义除了可以不用在模型里面定义获取器方法之外,还可以起到覆盖已经定义的获取器的作用,并且动态获取器可以支持Db类操作,弥补了Db操作不能使用获取器的缺憾,具体就看自己的需求来选择了。

Db::name('user')->withAttr('name', function($value, $data) {
return strtolower($value);
})->select();

总结

无论是获取器,还是之前提的修改器、搜索器,其作用无非是把你的模型工作细化和拆分,这样代码和逻辑也会更清晰,可维护性也大大增强,至于性能,从来不是模型首先考虑的。

PHP中文网,有大量免费的ThinkPHP入门教程,欢迎大家学习!

本文转自:https://blog.thinkphp.cn/825350


# thinkphp  # 都是  # 原始数据  # 如果你  # 的是  # 是一个  # 情况下  # 第二个  # 不存在  # 是否存在  # 自己的 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 详解Nginx + Tomcat 反向代理 如何在高效的在一台服务器部署多个站点  Python文本处理实践_日志清洗解析【指导】  用v-html解决Vue.js渲染中html标签不被解析的问题  Gemini手机端怎么发图片_Gemini手机端发图方法【步骤】  米侠浏览器网页背景异常怎么办 米侠显示修复  laravel怎么为应用开启和关闭维护模式_laravel应用维护模式开启与关闭方法  如何在阿里云购买域名并搭建网站?  韩国代理服务器如何选?解析IP设置技巧与跨境访问优化指南  Laravel怎么实现支付功能_Laravel集成支付宝微信支付  Javascript中的事件循环是如何工作的_如何利用Javascript事件循环优化异步代码?  深入理解Android中的xmlns:tools属性  Laravel如何设置定时任务(Cron Job)_Laravel调度器与任务计划配置  如何为不同团队 ID 动态生成多个非值班状态按钮  西安市网站制作公司,哪个相亲网站比较好?西安比较好的相亲网站?  Laravel任务队列怎么用_Laravel Queues异步处理任务提升应用性能  如何挑选最适合建站的高性能VPS主机?  HTML5打空格有哪些误区_新手常犯的空格使用错误【技巧】  如何用美橙互联一键搭建多站合一网站?  php打包exe后无法访问网络共享_共享权限设置方法【教程】  Angular 表单中正确绑定输入值以确保提交与验证正常工作  什么是JavaScript解构赋值_解构赋值有哪些实用技巧  网站广告牌制作方法,街上的广告牌,横幅,用PS还是其他软件做的?  如何用好域名打造高点击率的自主建站?  香港服务器租用每月最低只需15元?  Android实现代码画虚线边框背景效果  Windows10如何删除恢复分区_Win10 Diskpart命令强制删除分区  laravel怎么用DB facade执行原生SQL查询_laravel DB facade原生SQL执行方法  Laravel API路由如何设计_Laravel构建RESTful API的路由最佳实践  什么是javascript作用域_全局和局部作用域有什么区别?  百度输入法ai组件怎么删除 百度输入法ai组件移除工具  电商网站制作价格怎么算,网上拍卖流程以及规则?  Laravel怎么集成Log日志记录_Laravel单文件与每日日志配置及自定义通道【详解】  Laravel如何处理JSON字段的查询和更新_Laravel JSON列操作与查询技巧  如何使用 Go 正则表达式精准提取括号内首个纯字母标识符(忽略数字与嵌套)  如何在万网利用已有域名快速建站?  Laravel Seeder怎么填充数据_Laravel数据库填充器的使用方法与技巧  如何在自有机房高效搭建专业网站?  潮流网站制作头像软件下载,适合母子的网名有哪些?  香港服务器建站指南:免备案优势与SEO优化技巧全解析  Laravel怎么配置S3云存储驱动_Laravel集成阿里云OSS或AWS S3存储桶【教程】  Laravel怎么使用Collection集合方法_Laravel数组操作高级函数pluck与map【手册】  JavaScript常见的五种数组去重的方式  如何续费美橙建站之星域名及服务?  Laravel Livewire是什么_使用Laravel Livewire构建动态前端界面  Laravel Pest测试框架怎么用_从PHPUnit转向Pest的Laravel测试教程  JavaScript实现Fly Bird小游戏  Laravel怎么实现观察者模式Observer_Laravel模型事件监听与解耦开发【指南】  Edge浏览器如何截图和滚动截图_微软Edge网页捕获功能使用教程【技巧】  android nfc常用标签读取总结  齐河建站公司:营销型网站建设与SEO优化双核驱动策略