Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom error messages #677

Closed
rudiedirkx opened this issue Oct 15, 2021 · 6 comments
Closed

Custom error messages #677

rudiedirkx opened this issue Oct 15, 2021 · 6 comments

Comments

@rudiedirkx
Copy link

How do we feel about custom error message like some other libraries have:

[
	'type' => 'string',
	'pattern' => '^[sr]-\d+$',
	'message' => [
		'pattern' => 'Invalid sport/resource format, expected "s" or "r" + ID',
	],
],

Would use message.pattern if the pattern constraint failed.

I started with a PR for 5.2.11, but that's not how master works anymore, because every error is a ConstraintError now, which makes custom impossible by enum definition. Don't how how to approach it now. addError() knows the failed constraint, but not the element's schema/message item.

@infisamk
Copy link

infisamk commented Nov 9, 2021

I solved this problem with custom class:

class JSONValidator
{
    /** @var array */
    public $errors;

    /** @var array */
    private $messages;

    public function __construct()
    {
        $this->messages = array(
            "Invalid date (.*), expected format YYYY-MM-DD" => "Неверная дата %s, ожидаемый формат YYYY-MM-DD",
            "Invalid time (.*), expected format hh:mm:ss" => "Неверное время %s, ожидаемый формат hh:mm:ss",
            "Invalid date-time (.*), expected format YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss+hh:mm" => "Неверные дата-время, ожидаемый формат YYYY-MM-DDThh:mm:ssZ или YYYY-MM-DDThh:mm:ss+hh:mm",
            "Invalid time (.*), expected integer of milliseconds since Epoch" => "Неверное время %, ожидается количество миллисекунд с начала эпохи",
            "Invalid regex format (.*)" => "Ошибка в регулярном формате %s",
            "Invalid color (.*)" => "Ошибка в цвете %s",
            "Invalid style (.*)" => "Ошибка в стиле %s",
            "Invalid phone number (.*)" => "Ошибка в номере телефона %s",
            "Invalid URL format (.*)" => "Ошибка в формате URL %s",
            "Invalid email (.*)" => "Ошибка в email %s",
            "Invalid IP address (.*)" => "Ошибка в IP адресе %s",
            "Invalid hostname (.*)" => "Ошибка в имени хоста %s",
            "There must be a minimum of ([0-9]*) items in the array" => "В массиве должно быть не менее %u элементов",
            "There must be a maximum of ([0-9]*) items in the array" => "В массиве должно быть не более %u элементов",
            "There are no duplicates allowed in the array" => "В массиве не допускаются повторяющиеся значения",
            'The item (.*)\[(.*)\] is not defined and the definition does not allow additional items' => 'Элемент %1$s[%2$s] не определен, а дополнительные элементы не допустимы',
            "Does not have a value in the enumeration (.*)" => "Значение не соответствует перечисленным: %s",
            "Must have a minimum value of ([0-9]*)" => "Значение должно быть не меньше %u",
            "Use of exclusiveMinimum requires presence of minimum" => "Для использования exclusiveMinimum должен быть задан minimum",
            "Must have a maximum value of ([0-9]*)" => "Значение должно быть не больше %u",
            "Use of exclusiveMaximum requires presence of maximum" => "Для использования exclusiveMaximum должен быть задан minimum",
            "Is not divisible by (.*)" => "Не делится на %s",
            "Must be a multiple of (.*)" => "Должно быть кратным %s",
            'The pattern "(.*)" is invalid' => 'Шаблон "%s" содержит ошибки',
            "The property (.*) is not defined and the definition does not allow additional properties" => "Параметр %s не описан, а дополнительные параметры не допустимы",
            'The presence of the property (.*) requires that (.*) also be present' => 'Параметр %1$s обязателен, также как и %2$s',
            "Must contain a minimum of ([0-9]*) properties" => "Параметров должно быть не менее %u",
            "Must contain no more than ([0-9]*) properties" => "Параметров должно быть не более %u",
            "Schema is not valid" => "Некорректная схема",
            "Must be at most ([0-9]*) characters long" => "Количество символов должно быть не более %u",
            "Must be at least ([0-9]*) characters long" => "Количество символов должно быть не менее %u",
            "Does not match the regex pattern (.*)" => "Не соответствует регулярному шаблону %s",
            '(.*) value found, but (.*) is required' => 'Найдено значение с типом %1$s, но должно быть с типом %2$s',
            "The property (.*) is required" => "Параметр %s обязателен",
            "Disallowed value was matched" => "Была найдено запрещенное значение",
            "Matched a schema which it should not" => "Совпала схема, которой быть не должно",
            "Failed to match all schemas" => "Не соответствует всем схемам",
            "Failed to match at least one schema" => "Не соответствует хотя бы одной схеме",
            "Failed to match exactly one schema" => "Не соответствует только одной схеме",
            '(.*) depends on (.*) and (.*) is missing' => '%1$s зависит от %2$s, но %2$s отсутствует'
        );
    }

    public function validate($obj, $schema, $applyDefault = false)
    {
            $validator = new Validator();

            if ($applyDefault) {
                $validator->validate($obj, $schema, Constraint::CHECK_MODE_APPLY_DEFAULTS);
            } else {
                $validator->validate($obj, $schema);
            }

            if (!$validator->isValid()) {
                foreach ($validator->getErrors() as $error) {
                    $this->errors[] = sprintf("[%s] %s", $error['property'], $this->getTranslatedMessage($error['message']));
                }
                return false;
            } elseif ($strict) {
                return false;
            }
    }

    private function getTranslatedMessage($message)
    {
        foreach ($this->messages as $pattern => $translate) {
            if (preg_match('/^' . $pattern . '$/', $message, $matches)) {
                if (count($matches) == 1) {
                    return sprintf($translate, $matches[0]);
                }
                if (count($matches) == 2) {
                    return sprintf($translate, $matches[1]);
                }
                if (count($matches) == 3) {
                    return sprintf($translate, $matches[1], $matches[2]);
                }
                if (count($matches) == 4) {
                    return sprintf($translate, $matches[1], $matches[2], $matches[3]);
                }
            }
        }

        return $message;
    }
}

And use this:

$jv = new JSONValidator();
if (!$jv->validate($request, $schema, true)) {
      $messageError = array("Ответ не соответствует схеме");
      foreach ($jv->errors as $error) {
            $messageError[] = $error;
      }
      throw new Exception(implode("\n", $messageError));
}

@rudiedirkx
Copy link
Author

That doesn't allow for specific pattern error messages, or different messages for the same (e.g. date) validation.

@infisamk
Copy link

infisamk commented Nov 9, 2021

Can you give an example where this does not work?

@rudiedirkx
Copy link
Author

rudiedirkx commented Nov 10, 2021

The date error message (Invalid date (.*), expected format YYYY-MM-DD) is the same for every date field. I want to specify error messages in specific fields. Not per field type, but per individual field. Especically for pattern validation. Only a field specific message is useful for pattern errors.

See the OP example. That's a very specific message that can't be reused for any other pattern validation. Another might be

[
	'type' => 'string',
	'pattern' => '^\d?\d:\d\d$',
	'message' => [
		'pattern' => 'Invalid time. Format must be HH:MM',
	],
],

How do I enable both error messages, both for pattern validation, with your script?

@infisamk
Copy link

I thought you wanted to localize error messages, not add custom messages for specific errors. The Draft 4 JSON schema standard does not provide this. The library does not provide this either.

@DannyvdSluijs
Copy link
Collaborator

@rudiedirkx This seems very related to #444, which seems to be without a resolution at the time. It seems to be awaiting a v6 release for which I'm trying to revive the repository. It seems here is actually mentioning on how (in an abstract way) you can achieve custom error messages from the schema.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants