将自定义验证错误添加到 Laravel 表单

2022-08-30 16:53:47

我设置了一个基本表单以允许用户更改其电子邮件地址,并且在更改电子邮件之前,我正在对其进行以下验证:

// Set up the form validation
$validator = Validator::make(
    Input::all(),
    array(
        'email' => 'email|unique:users',
        'password' => 'required'
    )
);

// If validation fails, redirect to the settings page and send the errors
if ($validator->fails())
{
    return Redirect::route('settings')->withErrors($validator)->withInput();
}

这工作正常,但是在此基本验证之后,我想检查用户是否提供了正确的密码。为此,我正在使用Laravel的基本身份验证库执行以下操作:

// Find the user and validate their password
$user = Auth::user();

if (!Auth::validate(array('username' => $user->username, 'password' => Input::get('password'))))
{
    die("failed to authenticate");
}

与其自己处理逻辑来告诉用户他们的密码不正确,我宁愿只是在输入中添加表单错误,这样它就会像常规表单验证一样显示出来。像这样:password

if (!Auth::validate(array('username' => $user->username, 'password' => Input::get('password'))))
{
    $validator->addError('password', 'That password is incorrect.');
    return Redirect::route('settings')->withErrors($validator)->withInput();
}

这样,不正确的密码错误将显示在我的密码输入旁边,并且看起来像正确的表单验证。

我该怎么做?


答案 1

看看达伦·克雷格的答案。

不过,实现它的一种方法。

// inside if(Auth::validate)
if(User::where('email', $email)->first())
{
    $validator->getMessageBag()->add('password', 'Password wrong');
}
else
{
    $validator->getMessageBag()->add('email', 'Email not found');
}

答案 2

接受的答案(在我看来,以及Laravel的一般验证器)存在一个问题 - 验证过程本身和验证状态检测合并为一种方法。

如果您盲目地呈现袋子中的所有验证消息,那也没什么大不了的。但是,如果您有一些其他逻辑来检测验证程序是否失败并执行其他操作(例如为当前验证的表单字段提供国际文本消息),那么您就有问题了。

示范:

    // let's create an empty validator, assuming that we have no any errors yet
    $v = Validator::make([], []);

    // add an error
    $v->errors()->add('some_field', 'some_translated_error_key');
    $fails = $v->fails(); // false!!! why???
    $failedMessages = $v->failed(); // 0 failed messages!!! why???

    $v->getMessageBag()->add('some_field', 'some_translated_error_key');

产生相同的结果。为什么?因为如果你查看Laravel的验证器代码,你会发现以下内容:

public function fails()
{
    return ! $this->passes();
}

public function passes()
{
    $this->messages = new MessageBag;

如您所见,方法基本上清除了丢失您附加的所有消息的包,从而使验证程序假设没有错误。fails()

没有办法将错误追加到现有验证程序并使其失败。您只能创建一个包含自定义错误的新验证程序,如下所示:

    $v = Validator::make(['some_field' => null],
            ['some_field' => 'Required:some_translated_error_key']);
    $fails = $v->fails(); // true
    $failedMessages = $v->failed(); // has error for `required` rule

如果您不喜欢滥用自定义附加错误的验证规则的想法,您可以随时使用自定义规则扩展Laravel Validator。我添加了一个通用规则,并以这种方式使其成为强制性的:requiredfailkey

    // in custom Validator constructor: our enforced failure validator
    array_push($this->implicitRules, "Failkey");

    ...


/**
 * Allows to fail every passed field with custom key left as a message
 * which should later be picked up by controller
 * and resolved with correct message namespaces in validate or failValidation methods
 *
 * @param $attribute
 * @param $value
 * @param $parameters
 *
 * @return bool
 */
public function validateFailkey($attribute, $value, $parameters)
{
    return false; // always fails
}

protected function replaceFailkey($message, $attribute, $rule, $parameters)
{
    $errMsgKey = $parameters[0];

    // $parameters[0] is the message key of the failure
    if(array_key_exists($errMsgKey, $this->customMessages)){
        $msg = $this->customMessages[$parameters[0]];
    }       
    // fallback to default, if exists
    elseif(array_key_exists($errMsgKey, $this->fallbackMessages)){
        return $this->fallbackMessages[$parameters[0]];
    }
    else {
        $msg = $this->translator->trans("validation.{$errMsgKey}");
    }

    // do the replacement again, if possible
    $msg = str_replace(':attribute', "`" . $this->getAttribute($attribute) 
            . "`", $msg);

    return $msg;
}

我可以这样使用它:

    $v = Validator::make(['some_field' => null],
            ['some_field' => 'failkey:some_translated_error_key']);
    $fails = $v->fails(); // true
    $failedMessages = $v->failed(); // has error for `Failkey` rule

当然,这仍然是解决问题的一种黑客方式。

理想情况下,我会重新设计验证器,以将其验证阶段与状态检测(单独的方法和/或更好)明确分开,并添加方便的方法以手动失败具有特定规则的特定字段。虽然这也可能被认为是黑客,但是如果我们不仅想将Laravel验证器与Laravel自己的验证规则一起使用,而且还要使用我们的自定义业务逻辑规则,我们仍然别无选择。validate()passes()isValid()


推荐