前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Yii2和thinkphp5中一个小差异造成bug

Yii2和thinkphp5中一个小差异造成bug

作者头像
写PHP的老王
发布2019-08-12 15:27:50
9040
发布2019-08-12 15:27:50
举报
文章被收录于专栏:写PHP的老王写PHP的老王

考虑一个场景,一个函数需对相同表进行多次查询,多次查询中有部分查询条件相同。对于这种情况,Yii2和thinkphp5的实现方式要格外小心。在Yii2中,可以直接使用clone 复用共同的查询条件,但是thinkphp5的话,必须把相同条件再重复写一次。

例如,需要查询总有效文章数,以及今日发布有效文章数。

Yii2 版本

thinkphp5版本

如果在thinkphp5中使用clone会发生什么?

  • 1 clone model

执行过程没有报错,但是实际上是否真的正确呢?看一下执行的语句:

查询最终的执行时通过model类中的getQuery()方法获得的query对象执行的。所有的查询条件最终都绑定在query对象当中。

可以看出,clone model 之后,内部query其实还是同一个。虽然是在clone出来不同的两个model添加查询条件,但是最终都是添加在相同的query当做。 所以第一条语句就会有所有的查询条件。第二条语句没有任何条件的原因是因为query执行完之后,会把查询条件情空。

  • clone query

既然clone model不行,那直接clone内部query呢?

执行过程,抛出SQLSTATE[HY000]: General error: 2031错误信息,看看内部解析成什么样的语句了:

代码语言:javascript
复制
SELECT count(*) FROM `test` WHERE `status` = 1 
AND `is_delete` = 0;


SELECT count(*) FROM `test` WHERE 
`status` = :where_AND_status AND 
`is_delete` = :where_AND_is_delete AND
 `create_at` BETWEEN :where_AND_create_at_between_1 
AND :where_AND_create_at_between_2

初步认为是参数没有绑定上去。应该也是query内部引用了一个对象,对象在clone之后与原有对象是一个地址引用。通过一步一步断点输出,确认在$this->builder->select($options);之后获得了bind数据。因此只需要解绑clone前后对象的builder属性即可完成query对象的复制。查看query对象的属性,只有builder,connection是对象,但是connection我们希望在整个请求中是一个单实例,所以没必要区分。

最终修改,新建query子类,添加__clone方法,指定clone后对新对象执行php $this->setBuilder();保证 clone之后的builder是一个新实例。

到此,对于一开始的使用场景,thinkphp5也可以使用clone完成

在这其中有几点需要注意:

  • 对象clone之后,其属性执行的是浅拷贝!!
  • __clone()方法的操作只对clone出来新对象有效!
  • 如果没做任何修改,thinkphp5中不要直接clone model,除非自己知道在干什么,否则容易参数bug,因为它不抛错误。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-10-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 写PHP的老王 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档