Yii2事务try-catch场景下如何显示友好的表单验证错误
解决Yii2 ActiveForm跨表唯一验证前端生效及关联模型错误反馈问题
我来帮你搞定这两个问题,全程用Yii2原生方案,不用自己写自定义AJAX请求:
1. 让Customer模型的Email跨表唯一验证在提交前生效
你当前的Email唯一验证是关联User表的,Yii2默认不会为跨表的unique规则自动生成客户端验证逻辑,需要手动配置客户端验证回调:
修改Customer模型的验证规则
在rules()方法里更新Email的唯一验证规则,添加客户端验证相关配置:
[['email'], 'unique', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['email' => 'email'], 'message' => 'This email address has already been taken.', 'enableClientValidation' => true, 'clientValidate' => function($attribute, $params, $validator) { $validateUrl = \yii\helpers\Url::to(['customer/validate-email']); return <<<JS $.get('{$validateUrl}', { 'email': \$('input[name="Customer[email]"]').val() }).done(function(data) { if (data !== 'true') { messages.push("{$validator->message}"); } }); JS; } ],
添加验证Email唯一性的控制器Action
在Customer控制器里新增一个用于验证Email是否存在的Action:
public function actionValidateEmail() { $email = Yii::$app->request->get('email'); $isUnique = !User::find()->where(['email' => $email])->exists(); Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; return $isUnique; }
这样前端在输入Email时,会自动触发请求验证唯一性,实现提交前的实时验证效果。
2. 将User创建时的错误反馈到前端表单
你当前在customeruser方法里直接var_dump错误后exit,导致错误无法传递到Customer模型,前端自然看不到。需要把User的错误合并到Customer模型的错误集合中:
修改customeruser方法
不要直接exit,而是把User的错误添加到传入的Customer模型里:
public static function customeruser($model) { $user = new User(); $user->username = $model->email; $user->email = $model->email; $user->setPassword($model->legacy_customer_id); $user->generateAuthKey(); if (!$user->save()) { // 将User的错误映射到Customer模型对应属性 foreach ($user->getErrors() as $attr => $errors) { // 因为你用Email作为用户名,所以把username和email的错误都加到Customer的email属性上 $targetAttr = ($attr === 'username' || $attr === 'email') ? 'email' : null; foreach ($errors as $error) { $model->addError($targetAttr, $error); } } return null; } return $user; }
更新控制器的actionCreate逻辑
在控制器里判断User创建是否成功,如果失败就直接渲染表单,让错误显示在前端:
public function actionCreate() { $model = new Customer(); if ($model->load(Yii::$app->request->post())) { $transaction = Yii::$app->db->beginTransaction(); try { $user_create = \common\models\User::customeruser($model); // 如果User创建失败,直接返回表单并显示错误 if (!$user_create) { $transaction->rollBack(); if (Yii::$app->request->isAjax) { return $this->renderAjax('create', ['model' => $model]); } else { return $this->render('create', ['model' => $model]); } } $model->user_id = $user_create->id; $auth = \Yii::$app->authManager; $role = $auth->getRole('customer'); $auth->assign($role, $model->user_id); if ($model->save()) { $photo = UploadedFile::getInstances($model, 'uploads'); if ($photo !== null) { $save_images = \common\models\CustomerDocuments::save_document($model->user_id, $photo); } $transaction->commit(); return $this->redirect(['view', 'id' => $model->user_id]); } } catch (Exception $e) { $transaction->rollBack(); $model->addError(null, $e->getMessage()); } } // 原有渲染逻辑 if (Yii::$app->request->isAjax) { return $this->renderAjax('create', ['model' => $model]); } else { return $this->render('create', ['model' => $model]); } }
这样当创建User时出现用户名/邮箱重复错误,这些错误会被显示在前端的Email输入框下方,用户能直接看到问题所在。
额外确认
你的ActiveForm已经配置了enableClientValidation' => true和validateOnSubmit' => true,这部分是正确的,不需要修改。
内容的提问来源于stack exchange,提问作者rajwa766
相关产品推荐
相关产品推荐

