本文改进了Yii2中内置行为类TimestampBehavior,使得时间戳字段(如created_at,updated_at) 完全自己更新,方便得让你忘记它们的存在

Yii2的内置行为类TimestampBehavior几乎成了各种介绍Yii2行为的常客。各种讲解行为的文章中都少不了它的身影,它甚至登堂入室,被Yii2官方文档采用了。它的标准用法——虽然大家都知道——但我也贴出来:

  1. class User extends ActiveRecord
  2. {
  3. // ...
  4. public function behaviors()
  5. {
  6. return [
  7. [
  8. 'class' => TimestampBehavior::className(),
  9. 'attributes' => [
  10. ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
  11. ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
  12. ],
  13. // if you're using datetime instead of UNIX timestamp:
  14. // 'value' => new Expression('NOW()'),
  15. ],
  16. ];
  17. }
  18. }

但是这样做,有两个问题

  1. 如果你放上不存在的字段(比如将created_at拼写成create_at,或者多写了个字段logined_at),立即会报错,不信邪的可以试试
  2. 因为上一个原因的存在,你必须谨慎填写两个字段列表,不能多也不能错;而且不能放到公共的Model里面,因为可能User有updated_at而Comment里面就没有,那么岂不是每个Model都要去重载一遍behaviors()咯?

分析TimestampBehavior的源码确实可以印证我们说的两个问题,那么有解决办法吗?如何让时间戳字段智能起来,完全自动更新不用我们管?办法有的!请看下面,直接上改进后的源码:

  1. /**
  2. * 改进后的时间戳行为类
  3. *
  4. * 可以放到比如说common/behaviors/下面
  5. */
  6. class TimestampBehavior extends AttributeBehavior
  7. {
  8. //创建时要更新的字段(填写不存在的字段也无碍)
  9. public $onCreate = [];
  10. //更新时需要更新的字段
  11. public $onUpdate = [];
  12. public $value;
  13. /**
  14. * @inheritdoc
  15. */
  16. public function init()
  17. {
  18. parent::init();
  19. $this->onCreate = (array) $this->onCreate;
  20. $this->onUpdate = (array) $this->onUpdate;
  21. if (empty($this->attributes)) {
  22. //合并,去重复
  23. $this->attributes = [
  24. //创建时要更新的字段,需要去重复
  25. BaseActiveRecord::EVENT_BEFORE_INSERT => array_unique(array_merge($this->onCreate, $this->onUpdate)),
  26. //更新时要更新的字段
  27. BaseActiveRecord::EVENT_BEFORE_UPDATE => $this->onUpdate,
  28. ];
  29. }
  30. }
  31. // 关键就在这里——剔除不存在的字段
  32. public function evaluateAttributes($event)
  33. {
  34. if (!empty($this->attributes[$event->name])) {
  35. //删掉不存在的字段
  36. $this->attributes[$event->name] = array_intersect($this->owner->attributes(), $this->attributes[$event->name]);
  37. }
  38. parent::evaluateAttributes($event);
  39. }
  40. // 这里没做改变
  41. protected function getValue($event)
  42. {
  43. if ($this->value === null) {
  44. return time();
  45. }
  46. return parent::getValue($event);
  47. }
  48. // 这里也没做改变
  49. public function touch($attribute)
  50. {
  51. $this->owner->updateAttributes(array_fill_keys((array) $attribute, $this->getValue(null)));
  52. }
  53. }

那么在使用的时候,便可以这样来用:

  1. public function behaviors()
  2. {
  3. return [
  4. 'timeStamp' => [
  5. 'class' => TimestampBehavior::class,
  6. 'onCreate' => 'create_at',//写成['create_at']也可以的
  7. 'onUpdate' => ['update_at', 'login_at'],
  8. ]
  9. ];
  10. }

看起来好像简洁了很多,然而这不是最大优点,更精彩的在后面。

假如我们有张评论表comments,只有五个字段(id,content,status,create_at,update_at)一看就懂的,无需解释。现在创建一个新记录:

  1. $comment = new Comment();
  2. $comment->content = 'xiaomili';
  3. $comment->save();
  4. //打印结果
  5. VarDumper::dump(comment, 10, true);

可以看到:

  1. [
  2. 'id' => 129
  3. 'content' => 'xiaomili'
  4. 'status' => 1
  5. 'create_at' => 1520436115
  6. 'update_at' => 1520436115
  7. ]

再看更新

  1. $comment = Comment::findOne(129);
  2. $comment->content = 'this comment is updated';
  3. $comment->status = 2;
  4. $comment->save();
  5. //打印结果
  6. VarDumper::dump(comment, 10, true);

可以看到:

  1. [
  2. 'id' => 129
  3. 'content' => 'this comment is updated'
  4. 'status' => 2
  5. 'create_at' => 1520436115
  6. 'update_at' => 1520436209
  7. ]

我们注意到behaviors()中onUpdate指定的的login_at是不存在的,然而并没有错误被抛出,两个字段create_atupdate_at无论在创建还是在修改中都正常更新为最新时间戳。而如果comments表有login_at字段,它将会随着update_at一同被更新。

再看一种情况,我们拼写错误,在behaviors()中指定了一个不存在的时间戳字段:

  1. public function behaviors()
  2. {
  3. return [
  4. 'timeStamp' => [
  5. 'class' => TimestampBehavior::class,
  6. 'onCreate' => 'craeted_at',//注意这里拼写错误
  7. 'onUpdate' => ['update_at', 'login_at'],
  8. ]
  9. ];
  10. }

再创建一条新纪录看看:

  1. [
  2. 'id' => 130
  3. 'content' => 'xiaomili'
  4. 'status' => 1
  5. 'create_at' => 0
  6. 'update_at' => 1520436603
  7. ]

仍然可以成功创建,然而正确的字段create_at却没有被更新,顶多取个默认值而已。

上面几个例子说明我们改进的TimestampBehavior行为是有益无害的。因为它的人畜无害,好了,现在我们可以放到BaseModel里面供所有Model膜拜了:

  1. class BaseModel extends ActiveRecord
  2. {
  3. public function behaviors()
  4. {
  5. return [
  6. 'timeStamp' => [
  7. 'class' => TimestampBehavior::class,
  8. 'onCreate' => 'create_at'
  9. 'onUpdate' => ['update_at', 'login_at'],
  10. ]
  11. ];
  12. }
  13. }

原谅我更喜欢命名行为,因为容易解绑。

从此,你解放了大脑,不用再考虑每个表字段的不同而差异化的去设置onCreateonUpdate的不同字段了:comments表有create_at

update_at,而users表还有个login_at字段,但是category什么xxx_at字段也没有。它们的Model在创建/修改时也不会出半点幺蛾子。

当然,官方TimestampBehaviortouch()方法还是保留的,因为确实很方便:

  1. $user->touch('login_at');

有了这个改进的行为类,我们在开发时基本就不用去关心这些时间戳字段,只需要在用的时候拿来用就行。

笔者在开发的时候已经在多个项目中使用我们这个改进版的TimestampBehavior了,被证明是高效的,大家可以放心无虞的使用。

使用TimestampBehavior有多方便,我们来算一笔账:假如有一张非常普通的report表

13个字段——算中等水平。我们来分析下这些字段相对“关注程度”

  • 几乎不用关注的字段id,create_at,update_at,它们自己创建自动更新,一共3个,占23%
  • 需要一般关注的字段time,weight,height,systolic,diastolic,heartrate,bmi,vision,一般是和这个表内容字段,需要注意校验,一共8个,占62%
  • 需要重点关注的字段uid,pid因为它们是逻辑外键,是关联表的连接字段,在获取关联模型时是尤其要注意的,一共2个,占15%

这样,13个字段中,有3个字段你不用去考虑它们的赋值、校验;而weight,height等字段一般只会出现在自己的Model中,只需要在校验时注意下范围、大小、唯一性等特性,插入和修改关心下,查询时无需关心;而唯独uid,pid这个两个关联字段就比较麻烦,会出现在增删改以及关联查询中,想保持数据一致性就得特别小心。

先做这样的分析,然后就有的放矢。你的主要精力就放在那第二类和第三类上,并由其注意第三类。如果不是我们的TimestampBehavior,我们还得在第二类上再加上两个字段一共10个,要分析的字段从10个变成12个,多了20%。如果数据表的字段少这更加明显。鉴于几乎每张表都会有create_at,update_at这两个字段,假设我们有10张表,那我们就可以少分析20个字段,少花20份精力。减少重复工作才是高效之道啊!

忘记时间戳的存在——Yii2超实用的自动更新时间戳的Behavior(改进版)的更多相关文章

  1. mysql的时间戳说白了就俩问题,自动更新问题和不自动更新问题

    mysql的时间戳timestamp说白了就俩问题,自动更新问题和不自动更新问题

  2. 解决 Yii2 assets 不自动更新问题

    问题描述:core 里的 Asset (AssetBundle)更新 js 或 css 时,更新内容没有直接同步到其他模块 -- 如果想节约时间,直接拖到文章底部看结果就好~ 一.项目目录结构(大概介 ...

  3. MySQL 插入记录时自动更新时间戳

    将字段设置成timestamp类型,同时默认值设置成 CURRENT_TIMESTAMP.

  4. PostgreSQL之时间戳自动更新

    操作系统 :CentOS7.3.1611_x64 PostgreSQL版本 :9.6 问题描述 PostgreSQL执行Insert语句时,自动填入时间的功能可以在创建表时实现,但更新表时时间戳不会自 ...

  5. MySQL 自动插入、更新时间戳

    在 MySql 中,要做到自动出入当前时间戳,只要在定义表格时将字段的默认值设置为 CURRENT_TIMESTAMP 即可. 如: create table if not exists my_tab ...

  6. Mysql中,update语句引起的时间戳自动更新问题

    前几天遇到一个奇怪的问题. 在Mysql数据库中有一张表,表中有一个字段是timestamp类型的.我在update别的字段时,这个timestamp字段的时间会自动更新为当前时间. 后来发现,是My ...

  7. tk.mybatis通用插件updateByPrimaryKeySelective无法自动更新ON UPDATE CURRENT_TIMESTAMP列的解决办法

    tk.mybatis是一个很好用的通用插件,把CRUD这些基本的数据操作全都用动态SQL语句自动生成了,mapper和xml里十分清爽,但是昨天发现有一个小坑,记录在此: 有一张表,结构如下(已经简化 ...

  8. legend2---开发日志9(vue常见无法自动更新改变的原因是什么)

    legend2---开发日志9(vue常见无法自动更新改变的原因是什么) 一.总结 一句话总结:没找到变量,比如在computed属性中vue的变量没加this 没找到变量 1.函数中var bott ...

  9. EF-使用迁移技术让程序自动更新数据库表结构

    承接上一篇文章:关于类库中EntityFramework之CodeFirst(代码优先)的操作浅析 本篇讲述的是怎么使用迁移技术让程序自动通过ORM框架将模型实体类结构映射到现有数据库,并新增或修改与 ...

随机推荐

  1. Json----简单介绍

    Json 先分享一个网站http://www.bejson.com/,这个是用来检测Json文件的错误的,Json文件一般不好查找错误. 看懂Json只需要四句话: 对象表示为键值对 数据由逗号分隔 ...

  2. 在 Android 的文字编辑控件 (TEdit) 中, 如何按下 Enter 就隐藏虚拟键盘

    在 Windows 的应用中,我们常常为了让使用者能够快速输入,在Edit元件中的onKeyUp或者 onKeyDown 事件中主动侦测使用者输入的字元是否有换行符号 (Enter),当使用者按下了E ...

  3. CIKM 2012 papers to be downloaded

    http://dl.acm.org/citation.cfm?id=2398426   http://dl.acm.org/citation.cfm?id=2396825   http://dl.ac ...

  4. Data Center手册(3): Load Balancer

    Load Balancer的类型 DNS Round-Robin 这是一种很常见的分流的方式,具体配置如下: name server有一个zone文件,对于同一个domain,有多个IP www.ex ...

  5. Data Center手册(1):架构

    如图是数据中心的一个基本架构 最上层是Internet Edge,也叫Edge Router,也叫Border Router,它提供数据中心与Internet的连接. 连接多个网络供应商来提供冗余可靠 ...

  6. QEMU KVM Libvirt手册(11): Managing Storage

    When managing a VM Guest on the VM Host Server itself, it is possible to access the complete file sy ...

  7. TCP的三次握手过程与四次挥手

    TCP握手协议 在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接.第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确 ...

  8. 关于H5页面的测试总结与分析

    一.时下最流行的H5到底是什么 ?有什么优势和劣势? (1)H5 即HTML5,其实就是:移动端Web页面. (2)优势: H5可以跨平台使用,开发成本相对较低 H5可随时上线就更新版本,适合快速迭代 ...

  9. i春秋官网4.0上线啦 文末有福利

    爱瑞宝地(Everybody)期待了很久的 i春秋官网4.0上线啦 除了产品的功能更加完善 性能和体验也将大幅度提高 清新.舒适的视觉感受 搭配更加便捷的操作流程 只需一秒,扫码立即登录 即刻进入网络 ...

  10. [Swift]LeetCode37. 解数独 | Sudoku Solver

    Write a program to solve a Sudoku puzzle by filling the empty cells. A sudoku solution must satisfy  ...