问题
How do I handle generic validation errors in a Yii2 ActiveRecord model that do not relate to a specific attribute? For example I need to completely prohibit saving a model when a related record/modal has been set to inactive.
I can of course just pick a more or less random attribute and assign the error message to it, but what if the front end form doesn't have that attribute and therefore doesn't show the error? Or even worse, if a scenario later disables validation of that attribute (by not including it in the list of active attributes)?
I can of course return false in beforeSave()
or beforeValidate()
but then I have no option to specify a message to the user about why the model couldn't be saved, so I really don't like that idea.
Also, I don't want to throw Exceptions, it should just be a soft error message shown to the user.
What is the intended/best approach to handle this?
回答1:
have you looked into flash data they can be used for this purpose to show messages like success
, errors
and warnings
which are or are not specific to your model attributes. It's up to you where you want o use them.
I mostly insert data or save models within transaction block and there when I am saving multiple models I use try catch block to get the errors for any models that occur and use session flash along with ArrayHelper to add errors in the flash message.
For your purpose you can use it the following way.
Set the flash message with
Yii::$app->session->setFlash('error','you are not allowed to perform this message');
and you can get it using the following inside your view
if(Yii::$app->session->hasFlash('error')){
echo Yii::$app->session->gettFlash('error');
}
a more sophisticated approach towards this is \kartik\widgets\AlertBlock
install it using composer
php composer.phar require kartik-v/yii2-widget-alert "*"
Then create a file called alerts.php
in layouts
folder with the following code
use kartik\widgets\AlertBlock;
AlertBlock::widget (
[
'useSessionFlash' => false ,
'type' => AlertBlock::TYPE_GROWL ,
'alertSettings' => [
'settings' => [
'type' => kartik\widgets\Growl::TYPE_SUCCESS ,
'icon' => 'glyphicon glyphicon-ok-sign' ,
'title' => 'Note' ,
'showSeparator' => true ,
'body' => Yii::$app->session->getFlash ( 'success' )
] ,
]
] );
AlertBlock::widget (
[
'useSessionFlash' => false ,
'type' => AlertBlock::TYPE_GROWL ,
'alertSettings' => [
'settings' => [
'type' => kartik\widgets\Growl::TYPE_INFO ,
'icon' => 'glyphicon glyphicon-ok-sign' ,
'title' => 'Note' ,
'showSeparator' => true ,
'body' => Yii::$app->session->getFlash ( 'info' )
] ,
]
] );
AlertBlock::widget (
[
'useSessionFlash' => false ,
'type' => AlertBlock::TYPE_GROWL ,
'alertSettings' => [
'settings' => [
'type' => kartik\widgets\Growl::TYPE_DANGER ,
'icon' => 'glyphicon glyphicon-ok-sign' ,
'title' => 'Note' ,
'showSeparator' => true ,
'body' => Yii::$app->session->getFlash ( 'error' )
] ,
]
] );
AlertBlock::widget (
[
'useSessionFlash' => false ,
'type' => AlertBlock::TYPE_GROWL ,
'alertSettings' => [
'settings' => [
'type' => kartik\widgets\Growl::TYPE_DANGER ,
'icon' => 'glyphicon glyphicon-ok-sign' ,
'title' => 'Note' ,
'showSeparator' => true ,
'body' => Yii::$app->session->getFlash ( 'danger' )
] ,
]
] );
AlertBlock::widget (
[
'useSessionFlash' => false ,
'type' => AlertBlock::TYPE_GROWL ,
'alertSettings' => [
'settings' => [
'type' => kartik\widgets\Growl::TYPE_WARNING ,
'icon' => 'glyphicon glyphicon-ok-sign' ,
'title' => 'Note' ,
'showSeparator' => true ,
'body' => Yii::$app->session->getFlash ( 'warning' )
] ,
]
] );
and then include it in your layout file after $this->beginBody()
call like below
<?= Yii::$app->view->renderFile ( '@frontend/views/layouts/alerts.php' ); ?>
then you just have to set the flash message and that is it you won't even have to call getFlash()
, the extension will display it automatically you have the following variables available
- success
- info
- danger
- warning
- error
set using Yii::$app->session->setFlash('danger','You cannot do this');
EDIT:
Note: Whenever you are redirecting after setting the flash message remember to use return
along with redirect()
otherwise you may face the problem that the messages are not showing up.
return $this->redirect(['index']);
EDIT 2:
you are trying to add an error message for any particular model that is related to the model currently being saved and you want some soultion that would allow you to throw exceptions and show them in a nicely formatted error message, so the problem you are facing is setting the error message, if i would do it i would use the following approach. Lets say i have a actionTest()
like below which is saving the form on submission.
public function actionTest() {
$model = new Campaign();
if ($model->load(Yii::$app->request->post())) {
//start transaction block
$transaction = Yii::$app->db->beginTransaction();
try {
if (!$model->save()) {
//throw an exception if any errors
throw new \Exception(implode("<br />",\yii\helpers\ArrayHelper::getColumn($model->errors, 0,false)));
}
//commit the transaction if there arent any erorrs to save the record
$transaction->commit();
} catch (\Exception $ex) {
//roll back the transaction if any exception
$transaction->rollBack();
//catch the error and display with session flash
Yii::$app->session->setFlash('danger',$ex->getMessage());
}
}
$this->render('test');
}
回答2:
Another Option then picking a random attribute is to throw exceptions with a specific errorcode. Create a final class with Error Code constants with their messages. Then Put your save function call within a try Catch Block and Catch all specific exceptions and Return the Message to the Frontend
来源:https://stackoverflow.com/questions/48571647/yii2-validation-errors-that-are-not-attribute-specific