Yii2框架的几个隐蔽的坑
准备知识
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格式无效”的错误。而参考官方文档,发现这种格式是允许的官方文档。
啊啊啊。各种试错,最后发现如果改成php:Y-m-d
,世界就清净了。所以,如果你遇到这种问题,感激我吧。