1. The current implementation is as follows. When field validation fails, the API response always says: Database operation failed, please contact the administrator. This is a poor user experience. See Figure 1.

if ($animation->save()) {
return [
'code' => 10000,
'message' => '设置动画属性成功',
'data' => $animation,
];
}
return [
'code' => 10004,
'message' => '数据库操作失败,请联系管理员',
];
2. The desired implementation is as follows: 1. Do not expose database exceptions (such as SQL errors, PDO exceptions, etc.) — to avoid security risks. 2. Preserve model field validation errors (such as required, string length, etc.) — to provide clear feedback to the frontend.
3. Implementing a reusable BaseController::safeSave() method can help you uniformly handle model saving in Yii2, ensuring that: field validation errors return clear messages; SQL or system exceptions are not exposed to the frontend, but are instead logged and return a friendly message.
/**
* 安全保存模型:支持字段验证错误输出,系统异常捕获
*
* @param ActiveRecord $model 模型实例
* @param array $options 额外参数支持:
* - 'successMessage' 成功时返回的提示信息
* - 'failMessage' 验证失败时返回的提示信息
* - 'exceptionMessage' 异常时返回的提示信息
* - 'isUpdate' 是否更新操作(true/false)
*
* @return array
*/
protected function safeSave(ActiveRecord $model, array $options = []): array
{
$successMessage = $options['successMessage'] ?? '保存成功';
$failMessage = $options['failMessage'] ?? '字段验证失败';
$exceptionMessage = $options['exceptionMessage'] ?? '数据库操作失败,请联系管理员';
$isUpdate = $options['isUpdate'] ?? false;
try {
if (!$model->validate()) {
return [
'code' => 10200,
'message' => $failMessage . ': ' . ModelHelper::getFirstError($model),
];
}
if (!$isUpdate) {
$model->save(false); // insert
} elseif ($model->getDirtyAttributes()) {
// 如果是更新操作,检查是否变动再 save(false)
$model->save(false);
}
return [
'code' => 10000,
'message' => $successMessage,
'data' => $model,
];
} catch (Throwable $e) {
Yii::error([
'exception' => get_class($e),
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTraceAsString(),
], 'application.model.save.exception');
return [
'code' => 10500,
'message' => $exceptionMessage,
];
}
}
/api/helpers/ModelHelper.php
<?php
namespace api\helpers;
use yii\base\Model;
class ModelHelper
{
/**
* 获取模型验证失败时的第一个错误信息
* @param Model $model
* @return string
*/
public static function getFirstError(Model $model): string
{
// 验证失败:$errors 是一个包含错误信息的数组
$errors = $model->getErrors();
$firstMessage = '验证失败';
if (!empty($errors)) {
// 获取第一个属性名
$firstAttribute = array_key_first($errors);
$firstFieldErrors = $errors[$firstAttribute] ?? [];
// 获取第一个属性的第一个错误
$firstMessage = is_array($firstFieldErrors) && isset($firstFieldErrors[0])
? $firstFieldErrors[0]
: $firstMessage;
}
return $firstMessage;
}
}4. With the current implementation, when field validation fails, the API response reads: Field validation failed: Animation duration must be a number. This meets expectations. See Figure 2.

