yii2更新数据异常捕获需三者结合:try-catch捕获pdo级异常(如integrityexception)、save()返回值判断失败、geterrors()获取验证错误;save()默认不抛异常,仅返回false。

直接说结论:Yii2更新数据时的异常捕获,不能只靠try-catch包裹save(),必须结合errorHandler配置、模型getErrors()校验、以及底层ActiveRecord::update()返回值判断,三者缺一不可。
更新失败时save()返回false但没抛异常?
这是最常被忽略的点:Yii2的save()默认不抛异常,验证失败或SQL执行失败都只返回false。你如果只写try { $model->save(); },根本捕获不到任何东西。
- 验证失败(如required字段为空)→
save()返回false,错误存于$model->getErrors() - 数据库约束冲突(如唯一索引重复)→
save()返回false,$model->getErrors()可能为空,需查$model->getFirstError()或看日志 - 手动调用
update()且$runValidation = false→ 即使SQL报错,也只返回0,不会抛Exception
哪些异常真会抛出来?怎么捕获?
只有底层PDO操作触发PHP级异常时才会真正抛出,比如连接中断、权限不足、表不存在。这类异常属于yii\db\Exception及其子类(如yii\db\IntegrityException),必须用try/catch包住save()或update()。
- 捕获要具体:
catch (\yii\db\IntegrityException $e)比catch (\Exception $e)更安全,避免吞掉不该处理的异常 - 注意:事务中抛异常会自动回滚,但若你手动
commit()后再出错,得自己补rollback() - 不要在
catch里直接echo $e->getMessage()——生产环境必须过滤敏感信息,比如数据库连接串、表名、字段名
为什么配了errorHandler还是看不到错误页?
因为errorHandler只处理「未捕获的全局异常」,而你在代码里已经try/catch了,异常没往上冒,自然不会触发errorAction跳转。
- 想让404/500页生效,得让异常逃出当前作用域——要么不
catch,要么catch后重新throw - 常见陷阱:
catch里写了Yii::error($e)就以为完事了,结果用户看到白屏或500,后台却没走error.php - 验证类业务错误(如“库存不足”)不该抛异常,应走
return ['success' => false, 'msg' => '库存不足'],和系统级异常严格区分
API场景下统一返回JSON格式的坑
纯API项目不能依赖site/error视图,必须重写convertExceptionToResponse()或拦截handleException(),否则404会返回HTML页面,前端解析失败。
- 推荐做法:自定义
errorHandler类,继承yii\web\ErrorHandler,重写renderException(),对Yii::$app->request->isAjax || Yii::$app->request->getIsJson()返回JSON - 别忘了设置
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON,否则header仍是text/html - 关键细节:
renderException()必须return字符串或数组,不能echo或exit,否则Response组件无法接管输出
真正难的不是写catch块,而是分清哪些该由模型验证拦住、哪些该由数据库约束拦住、哪些该由业务逻辑抛出自定义异常、哪些必须让框架errorHandler兜底——边界模糊的地方最容易漏日志、错分类、误响应。











