Yii2:在事务中使用 try catch 时显示用户友好的验证错误
Yii2: show user friendly validation errors when using try catch with transactions
我正在使用 bootstrap ActiveForm
。这是我的表格:
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use kartik\file\FileInput;
/* @var $this yii\web\View */
/* @var $model common\models\Customer */
/* @var $form yii\widgets\ActiveForm */
?>
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data'],
'id' => 'customer-form',
'enableClientValidation' => true,
'options' => [
'validateOnSubmit' => true,
'class' => 'form'
],
'layout' => 'horizontal',
'fieldConfig' => [
'horizontalCssClasses' => [
'label' => 'col-sm-4',
// 'offset' => 'col-sm-offset-2',
'wrapper' => 'col-sm-8',
],
],
]); ?>
<?= $form->field($model, 'email')->textInput(['maxlength' => true]) ?>
<?php ActiveForm::end(); ?>
这是我的模型:
class Customer extends \yii\db\ActiveRecord
{
public $username;
public $password;
public $status;
public $email;
public $uploads;
public function rules()
{
return [
[['user_id', 'created_by', 'updated_by'], 'integer'],
[['created_at','uploads', 'updated_at','legacy_customer_id','fax','phone_two','trn'], 'safe'],
[['company_name','customer_name','username','password', 'tax_id'], 'string', 'max' => 200],
[['customer_name','email','legacy_customer_id','company_name','city'], 'required'],
[['is_deleted','status'], 'boolean'],
[['address_line_1', 'state','phone', 'country'], 'string', 'max' => 450],
[['address_line_2', 'city', 'zip_code'], 'string', 'max' => 45],
[['user_id','legacy_customer_id'], 'unique'],
['email', 'email'],
[['uploads'], 'file', 'maxFiles' => 10],
[['email'], 'unique', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['email' => 'email'], 'message' => 'This email address has already been taken.'],
[['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['user_id' => 'id']],
];
}
}
Here is Controller
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);
if ($user_create) {
$model->user_id = $user_create->id;
$auth = \Yii::$app->authManager;
$role = $auth->getRole('customer');
$auth->assign($role, $model->user_id);
}
if ($user_create && $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();
}
}
if (Yii::$app->request->isAjax) {
return $this->renderAjax('create', [
'model' => $model,
]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
现在规则中使用的必需属性正在工作。它不允许表单提交,直到必填字段填充了一些值,但同时使用目标 class 的唯一属性不起作用并允许表单提交。单击提交表单后,表单不会提交,但不会显示唯一验证的错误。关于我在 bootstrap 模态中使用表单,我希望表单在提交之前显示唯一的提交错误,就像所需的工作一样。我可以在模糊功能上使用 jQuery 并发送自定义 AJAX 请求,但我想要 Yii 2.
的默认解决方案
EDIT
这是由于未保存用户而引发错误的地方
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 () ) {
var_dump ( $user->getErrors () );
exit ();
} return $user->save () ? $user : null;
}
var_dump()
显示以下
'username' => array (size = 1)
0 => string 'This username has already been taken.' (length = 37)
'email' => array (size = 1) 0 => string 'This email address has already been taken.' (length = 42).
有两三件事:
1. 您不接受用户输入的电子邮件地址。
2. 如果在数据库中您的字段被视为 NULL
它将插入一个空白条目
3. 在第二次插入时,如果数据库字段不是 NUll.
,它将显示错误消息
解法:
1. 使数据库字段不为空。
2. 将电子邮件作为用户输入。
3.尝试打印错误
if ($model->validate()) {
// all inputs are valid
} else {
// validation failed: $errors is an array containing error messages
$errors = $model->errors;
}
4. 删除 'skipOnError' => true,
试试上面的我相信你会得到解决方案
当您将 try catch 块与事务一起使用时,您应该抛出并捕获诸如异常之类的错误,以便回滚事务,并将消息也显示给用户。
你不是在消费或使用 try{}catch(){}
块的美丽与交易。您应该始终抛出 Exception
以防任何模型未保存并且 catch 块将回滚事务。
例如,您通过调用
将用户保存在函数 customeruser()
中
$user_create = \common\models\User::customeruser($model);
和 returning user
对象或 null
否则,然后在下一行中,您将验证用户是否已创建。
if ($user_create) {
如果模型未保存,您应该简单地从函数 customeruser()
中抛出异常,并且 return $user
对象,否则,您不必检查 $user_create
再次验证用户是否未保存,将抛出异常并将控制权转移到 catch
块,并且永远不会调用 $user_create = \common\models\User::customeruser($model);
之后的行。
当我有多个模型要保存并且我正在使用事务块时,我主要使用以下方式。
$transaction = Yii::$app->db->beginTransaction ();
try {
if ( !$modelUser->save () ) {
throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $modelUser->errors , 0 , false ) ) );
}
if ( !$modelProfile->save () ) {
throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $modelProfile->errors , 0 , false ) ) );
}
$transaction->commit();
} catch ( \Exception $ex ) {
$transaction->rollBack();
Yii::$app->session->setFlash ( 'error' , $ex->getMessage () );
}
所以你可以对你的代码做同样的事情
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()){
throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $user->errors , 0 , false ) ) );
}
return $user;
}
将您的 actionCreate
更改为以下内容
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 );
$model->user_id = $user_create->id;
$auth = \Yii::$app->authManager;
$role = $auth->getRole ( 'customer' );
$auth->assign ( $role , $model->user_id );
if ( !$model->save () ) {
throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $model->errors , 0 , false ) ) );
}
$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 $ex ) {
Yii::$app->session->setFlash ( 'error' , $ex->getMessage () );
$transaction->rollBack ();
}
}
if ( Yii::$app->request->isAjax ) {
return $this->renderAjax ( 'create' , [
'model' => $model ,
] );
}
return $this->render ( 'create' , [
'model' => $model ,
] );
}
我正在使用 bootstrap ActiveForm
。这是我的表格:
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use kartik\file\FileInput;
/* @var $this yii\web\View */
/* @var $model common\models\Customer */
/* @var $form yii\widgets\ActiveForm */
?>
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data'],
'id' => 'customer-form',
'enableClientValidation' => true,
'options' => [
'validateOnSubmit' => true,
'class' => 'form'
],
'layout' => 'horizontal',
'fieldConfig' => [
'horizontalCssClasses' => [
'label' => 'col-sm-4',
// 'offset' => 'col-sm-offset-2',
'wrapper' => 'col-sm-8',
],
],
]); ?>
<?= $form->field($model, 'email')->textInput(['maxlength' => true]) ?>
<?php ActiveForm::end(); ?>
这是我的模型:
class Customer extends \yii\db\ActiveRecord
{
public $username;
public $password;
public $status;
public $email;
public $uploads;
public function rules()
{
return [
[['user_id', 'created_by', 'updated_by'], 'integer'],
[['created_at','uploads', 'updated_at','legacy_customer_id','fax','phone_two','trn'], 'safe'],
[['company_name','customer_name','username','password', 'tax_id'], 'string', 'max' => 200],
[['customer_name','email','legacy_customer_id','company_name','city'], 'required'],
[['is_deleted','status'], 'boolean'],
[['address_line_1', 'state','phone', 'country'], 'string', 'max' => 450],
[['address_line_2', 'city', 'zip_code'], 'string', 'max' => 45],
[['user_id','legacy_customer_id'], 'unique'],
['email', 'email'],
[['uploads'], 'file', 'maxFiles' => 10],
[['email'], 'unique', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['email' => 'email'], 'message' => 'This email address has already been taken.'],
[['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['user_id' => 'id']],
];
}
}
Here is Controller
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);
if ($user_create) {
$model->user_id = $user_create->id;
$auth = \Yii::$app->authManager;
$role = $auth->getRole('customer');
$auth->assign($role, $model->user_id);
}
if ($user_create && $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();
}
}
if (Yii::$app->request->isAjax) {
return $this->renderAjax('create', [
'model' => $model,
]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
现在规则中使用的必需属性正在工作。它不允许表单提交,直到必填字段填充了一些值,但同时使用目标 class 的唯一属性不起作用并允许表单提交。单击提交表单后,表单不会提交,但不会显示唯一验证的错误。关于我在 bootstrap 模态中使用表单,我希望表单在提交之前显示唯一的提交错误,就像所需的工作一样。我可以在模糊功能上使用 jQuery 并发送自定义 AJAX 请求,但我想要 Yii 2.
的默认解决方案EDIT
这是由于未保存用户而引发错误的地方
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 () ) {
var_dump ( $user->getErrors () );
exit ();
} return $user->save () ? $user : null;
}
var_dump()
显示以下
'username' => array (size = 1) 0 => string 'This username has already been taken.' (length = 37) 'email' => array (size = 1) 0 => string 'This email address has already been taken.' (length = 42).
有两三件事:
1. 您不接受用户输入的电子邮件地址。
2. 如果在数据库中您的字段被视为 NULL
它将插入一个空白条目3. 在第二次插入时,如果数据库字段不是 NUll.
,它将显示错误消息解法: 1. 使数据库字段不为空。
2. 将电子邮件作为用户输入。
3.尝试打印错误
if ($model->validate()) {
// all inputs are valid
} else {
// validation failed: $errors is an array containing error messages
$errors = $model->errors;
}
4. 删除 'skipOnError' => true,
试试上面的我相信你会得到解决方案
当您将 try catch 块与事务一起使用时,您应该抛出并捕获诸如异常之类的错误,以便回滚事务,并将消息也显示给用户。
你不是在消费或使用 try{}catch(){}
块的美丽与交易。您应该始终抛出 Exception
以防任何模型未保存并且 catch 块将回滚事务。
例如,您通过调用
将用户保存在函数customeruser()
中
$user_create = \common\models\User::customeruser($model);
和 returning user
对象或 null
否则,然后在下一行中,您将验证用户是否已创建。
if ($user_create) {
如果模型未保存,您应该简单地从函数 customeruser()
中抛出异常,并且 return $user
对象,否则,您不必检查 $user_create
再次验证用户是否未保存,将抛出异常并将控制权转移到 catch
块,并且永远不会调用 $user_create = \common\models\User::customeruser($model);
之后的行。
当我有多个模型要保存并且我正在使用事务块时,我主要使用以下方式。
$transaction = Yii::$app->db->beginTransaction ();
try {
if ( !$modelUser->save () ) {
throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $modelUser->errors , 0 , false ) ) );
}
if ( !$modelProfile->save () ) {
throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $modelProfile->errors , 0 , false ) ) );
}
$transaction->commit();
} catch ( \Exception $ex ) {
$transaction->rollBack();
Yii::$app->session->setFlash ( 'error' , $ex->getMessage () );
}
所以你可以对你的代码做同样的事情
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()){
throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $user->errors , 0 , false ) ) );
}
return $user;
}
将您的 actionCreate
更改为以下内容
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 );
$model->user_id = $user_create->id;
$auth = \Yii::$app->authManager;
$role = $auth->getRole ( 'customer' );
$auth->assign ( $role , $model->user_id );
if ( !$model->save () ) {
throw new \Exception ( implode ( "<br />" , \yii\helpers\ArrayHelper::getColumn ( $model->errors , 0 , false ) ) );
}
$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 $ex ) {
Yii::$app->session->setFlash ( 'error' , $ex->getMessage () );
$transaction->rollBack ();
}
}
if ( Yii::$app->request->isAjax ) {
return $this->renderAjax ( 'create' , [
'model' => $model ,
] );
}
return $this->render ( 'create' , [
'model' => $model ,
] );
}