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

Joh*_*ean 14 php forms validation laravel laravel-4

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

// 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();
}
Run Code Online (Sandbox Code Playgroud)

这工作正常,但在此基本验证后,我想检查用户是否提供了正确的密码.为此,我正在使用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");
}
Run Code Online (Sandbox Code Playgroud)

而不是处理逻辑告诉用户他们的密码本身不正确,我宁愿只是在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();
}
Run Code Online (Sandbox Code Playgroud)

这样,我的密码输入旁边会显示错误的密码错误,看起来像是正确的表单验证.

我怎样才能做到这一点?

Bas*_*ann 33

见Darren Craig的回答.

但是实现它的一种方法.

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


Jus*_*tin 11

接受的答案有一个问题(在我看来,一般是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???
Run Code Online (Sandbox Code Playgroud)

也,

    $v->getMessageBag()->add('some_field', 'some_translated_error_key');
Run Code Online (Sandbox Code Playgroud)

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

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

public function passes()
{
    $this->messages = new MessageBag;
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,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
Run Code Online (Sandbox Code Playgroud)

如果您不喜欢滥用required自定义附加错误的验证规则,则可以始终使用自定义规则扩展Laravel Validator.我添加了一个通用failkey规则,并以此方式强制执行:

    // 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;
}
Run Code Online (Sandbox Code Playgroud)

我可以像这样使用它:

    $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
Run Code Online (Sandbox Code Playgroud)

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

理想情况下,我会重新设计验证明确分开状态检测的验证阶段(单独的方法validate()passes()或更好isValid()),并添加方便的方法来手动故障与特定规则的特定领域.虽然这也可能被认为是hacky,但如果我们想要使用Laravel验证器不仅仅使用Laravel自己的验证规则,而且还有我们的自定义业务逻辑规则,我们别无选择.

  • “无法将错误附加到现有验证器并使其失败”......有!您可以使用所谓的“[验证后挂钩](https://laravel.com/docs/5.6/validation#after-validation-hook)”。它在最初提出问题时不可用,但自 Larvel 5.3 以来一直存在。 (3认同)

TKo*_*KoL 6

用户 Matt K 在评论中表示,laravel 已经实现了验证钩子,这正是我们想要的:

$validator = Validator::make(...);

$validator->after(function ($validator) {
    if ($this->somethingElseIsInvalid()) {
        $validator->errors()->add('field', 'Something is wrong with this field!');
    }
});

if ($validator->fails()) {
    // this actually runs! even if the original validator succeeded!
}
Run Code Online (Sandbox Code Playgroud)