public function validate(Request $request, array $rules, array $messages = [])
    {
        foreach ($rules as $param => $options) {
            $value = $request->getParam($param);
            $this->data[$param] = $value;
            $isRule = $options instanceof V;

            try {
                if ($isRule) {
                    $options->assert($value);
                } else {
                    if (!isset($options['rules']) || !($options['rules'] instanceof V)) {
                        throw new InvalidArgumentException('Validation rules are missing');
                    }

                    $options['rules']->assert($value);
                }
            } catch (NestedValidationException $e) {
                $paramRules = $isRule ? $options->getRules() : $options['rules']->getRules();

                // Get the names of all rules used for this param
                $rulesNames = [];
                foreach ($paramRules as $rule) {
                    $rulesNames[] = lcfirst((new ReflectionClass($rule))->getShortName());
                }

                // If the 'message' key exists, set it as only message for this param
                if (!$isRule && isset($options['message']) && is_string($options['message'])) {
                    $this->errors[$param] = [$options['message']];
                    return $this;
                } else { // If the 'messages' key exists, override global messages
                    $params = [
                        $e->findMessages($rulesNames)
                    ];

                    // If default messages are defined
                    if (!empty($this->defaultMessages)) {
                        $params[] = $e->findMessages($this->defaultMessages);
                    }

                    // If global messages are defined
                    if (!empty($messages)) {
                        $params[] = $e->findMessages($messages);
                    }

                    // If individual messages are defined
                    if (!$isRule && isset($options['messages'])) {
                        $params[] = $e->findMessages($options['messages']);
                    }

                    $this->errors[$param] = array_values(array_filter(call_user_func_array('array_merge', $params)));
                }
            }
        }

        return $this;
    }