yii2: Adding common validation error (not related with particular attribute)
When writing a custom validation, we need to use addError method of yii\base\Model (if we write it in model) or yii\validators\Validator (if we write separate validator).
Both methods require $attribute parameter (attribute name) to be passed, and as we can see from the sources, we can’t leave it blank:
addError of yii\base\Model:
/**
* Adds a new error to the specified attribute.
* @param string $attribute attribute name
* @param string $error new error message
*/
public function addError($attribute, $error = '')
{
$this->_errors[$attribute][] = $error;
}
addError of yii\validators\Validator:
/**
* Adds an error about the specified attribute to the model object.
* This is a helper method that performs message selection and internationalization.
* @param \yii\base\Model $model the data model being validated
* @param string $attribute the attribute being validated
* @param string $message the error message
* @param array $params values for the placeholders in the error message
*/
public function addError($model, $attribute, $message, $params = [])
{
$params['attribute'] = $model->getAttributeLabel($attribute);
if (!isset($params['value'])) {
$value = $model->$attribute;
if (is_array($value)) {
$params['value'] = 'array()';
} elseif (is_object($value) && !method_exists($value, '__toString')) {
$params['value'] = '(object)';
} else {
$params['value'] = $value;
}
}
$model->addError($attribute, Yii::$app->getI18n()->format($message, $params, Yii::$app->language));
}
Possible options:
1) Select the most important relevant field and add error to it.
2) Select multiple important relevant fields and add the same error message to them (we can store and pass message in separate variable before passing to keep code DRY).
3) We can use not existing attribute name for adding error, let’s say all, because attribute existence is not checked at that point.
class YourForm extends \yii\base\Model
{
/**
* @inheritdoc
*/
public function rules()
{
return [
['name', 'yourCustomValidationMethod'],
];
}
/**
* @return boolean
*/
public function yourCustomValidationMethod()
{
// Perform custom validation here regardless of "name" attribute value and add error when needed
if (...) {
$this->addError('all', 'Your error message');
}
}
}
Note that we still need to attach validator to existing attribute(s) (otherwise exception will be thrown). We can use the most relevant attribute.
As a result, we will see error only in error summary. We can display error summary in form like that:
<?= $form->errorSummary($model) ?>
But in most cases there are always one or multiple attributes related with error so we can use option 1 or 2. Option 3 is kind of hack but I think it’s pretty good workaround for that problem.
Please also see this related question and my answer on StackOverflow, basically the text above was taken from there.
Bottom line, I think we either need:
1) Add support for adding common validation errors. For example Django has support for that. 2) If you find implementing this ambiguous or not necessary because it goes against some framework philosophy or principles, I think the solution described in option 3 proves that validation errors implementation is a bit inconsistent in terms of passing attribute.
- We can’t pass not existing attribute when declaring validation rule.
- But we can’t easily add error for non existing attribute.
At least we can get rid of that.
Also according docs section would be helpful.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 6
- Comments: 23 (23 by maintainers)
Commits related to this issue
- Closed #12778: Added docs for multiple attributes validation (#13005) * Added docs for mutiple attributes validation * Fixed typo in formatting [skip ci] * More precise explanation about count ... — committed to yiisoft/yii2 by arogachev 8 years ago
- Closed #12778: Added docs for multiple attributes validation (#13005) * Added docs for mutiple attributes validation * Fixed typo in formatting [skip ci] * More precise explanation about count ... — committed to SilverFire/yii2 by arogachev 8 years ago
I am against such change: it breaks the model workflow.
Modelhas been designed as a data storage, each atttribute of which can be acquired from user input and thus should be checked to be valid.Consider following problems:
Model::validate()accept list of attributes to be validated. How you can trigger your “model-wide” validation here?Validator::$skipOnError, allowing to skip validation of attribute if it is already have an error, like there is no necessity to check if value is integer if it is empty. How will “model-wide” validator behave itself in this scope?Consider a common example of ‘model-wide’ validation: there are 2 fields at contact form: ‘phone’ and ‘email’. At least one of them should be filled, but if one has been filled - the other one can be left blank. If both fields are empty they both(!) should be highlighted indicating there is a error, should not they?
My opinoin PR #12829 overcomplicates existing code without much purpose.
Instead of creating validator, which will work without attribute, we should create validator, which handles several attributes!
There is already a method
Validator::validateAttributes(), which accepts list of attributes and can process them as batch.You can easily solve the “model-wide” validation issue creating your own validator with custom
validateAttributes()implementation and assign it to all attributes:I can not see anything bad in it: there should be some way to present your ‘model-wide’ error to the user - place error message to the particular place inside HTML page. You can use
*for it if you want - it whould be just #12829 solution, isn’t it?I think it is a good idea. I’ve encountered the need for model-wide error not bound to attributes myself. Was choosing the most significant attribute in the case but logically these were general errors.
@SilverFire OK. Can I add examples for it to the docs in this case?
@arogachev github does not reference issue if you type the id in the title. if you type the ID in the PR description it will genereate a reference.
isnt this already possible as $model->addError() is not bound to attributes.
You can already put your own errorType in there. So its up to you to do the naming:
@SilverFire OK, I’ll send a PR soon.
Sure, it will be a good solution. Thank you for understanding