Yii2框架的几个隐蔽的坑
摘要:Yii2是一款优秀的通用Web后端框架,结构简单优雅、实用功能丰富、扩展性强、性能搞是他最突出的优点。它优秀的地方你在使用过程中总能轻易的发现,无须赘述。而这些隐蔽的小瑕疵,显得更有必要告诉大家。
博客: http://www.cnblogs.com/jhzhu
作者: 知明所以
时间: 2015-08-17
目录
说点闲话
距离上次写博客,已经有三个月了。在动手写之前,总是带着深深的罪恶感。被它折磨许久,终于,还是,动手了。
值得庆祝的一件事:最近开始,每天早上8:30起来健身了。有两个视频很好用,只需8分钟,照着做一遍保证你(生)爽(不)到(如)爆(死)。(8分钟腹肌锻炼第2级-下载,8分钟胸肌锻炼第2级-下载)
值得反思的一件事:最近看了《叔本华美学随笔》,改变了我一直以来对阅读的看法。我曾经以为阅读是进步的源动力,却被这本书深深的打脸了。来,先给大家分享一段:
我 们只管所见的外在环境并不像阅读物那样,把某已确定的见解强加给我们的头脑,而只是为我们提供了素材和机会。去思考与我们的头脑能力相称、与当下的情绪相 符的事情。所以,太多的阅读会是我们的精神失去弹性,就像把一重物持续压在一条弹簧上面就会是弹簧失去弹性一样;而让自己没有自己思想的最稳妥的办法就是 在空闲的每一分钟马上随手拿起一本书。
思考才是进步的源动力!
好了,扯淡完毕,步入正题。
ActiveRecord被莫名写入?
准备知识
ActiveRecord的基本用法。如果不理解,可参考这里。
代码现场
/**
* @property integer $id
* @property string $name
* @property string $detail
* @property double $price
* @property integer $area
**/
class OcRoom extends ActivieRecord
{
...
}
$room = OcRoom::find() //先取出一个对象。
->select(['id']) //只取出'id'列
->where(['id'=>20])
->one();
$room->save(); //保存,会发现此行的其它字段都被写成默认值了。
总结问题
这个例子的问题在于:
我从数据库中取出了一行,也就是代码中的$room,但是只取出了id字段,而其他字段自然就是默认值。
当我$room->save()的时候,那些是默认值的字段也被保存到数据库里去了。what!?
也就是说,当你想节约资源,不取出所有字段的时候,一定要注意不能保存,否则,很多数据会被莫名修改为默认值。
解决方法
然而,我们有什么解决办法呢?提供几种思路:
自己时刻注意,避免未完全取出的ActiveRecord的保存。
修改或继承ActiveRecord, 使得,当此对象由find()新建,且字段没有完全取出,调用save()方法,抛出异常。
修改或继承ActiveRecord,使得,当此对象由find()新建,且字段没有完全取出,调用save()方法时,只保存取出过的字段,其他字段被忽略。
你的Transaction生效了吗?
代码现场
/**
* @property integer $id
* @property string $name
**/
class OcRoom extends ActiveRecord
{
public function rules()
{
return [['name','string','min'=>2,'max'=>10]];
}
...
}
class OcHouse extends ActiveRecord
{
public function rules()
{
return [['name','string','max'=>10]];
}
...
}
$a = new OcRoom();
$a->name = ''; //name为空字符串,不满足rules()条件。
$b = new OcHouse();
$b->name = '我的房间'; //name合法,可以保存。
$transaction = Yii::$app->db->beginTransaction();
try{
$a->save(); //name字段不合法,无法验证通过,在validate()阶段已经返回false,不会进行数据库存储的步骤,所以也不会抛出异常。
$b->save(); //name字段合法,可以正常保存。
$transaction->commit(); //提交后,发现$a保存失败,而$b保存成功。
}
catch (Exception $e)
{
Yii::error($e->getTraceAsString(),__METHOD__);
$transaction->rollBack();
}
问题总结
这段代码的问题在于:
大家知道$transaction的存在意义是保证整段数据库存储代码要么全成功,要么全失败。
显然,在这个例子中,transaction并没有达到我们想要的效果:$a因为validate()都没过,所以$transation->commit()的时候并不会报错。
解决方法
在$transation块内,所有的save()都要判断下返回值,如果为false,则直接抛出异常。
'Y-m-d'不被识别?
代码现场
OcRenterBill extends ActiveRecord
{
public function rules()
{
return [
['start_time','date','format'=>'Y-m-d'],
];
}
}
$a = new OcRenterBill();
$a = '2015-09-12';
$a->save(); //会报错,说格式不对。
问题总结
如果一开始,Yii框架就报错,这个还不算坑。坑的是我在Mac上开发时,这个可以完全正常的工作,而发布到线上环境(Ubuntu)后,就弹出“属性start_time格式无效”的错误。而参考官方文档,发现这种格式是允许的官方文档。