当前位置: 首页 > 知识库问答 >
问题:

Laravel 5.4-如何对同一自定义验证规则使用多条错误消息

山乐生
2023-03-14

为了重用代码,我在名为ValidatorServiceProvider的文件中创建了自己的验证器规则:

class ValidatorServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Validator::extend('checkEmailPresenceAndValidity', function ($attribute, $value, $parameters, $validator) {
            $user = User::where('email', $value)->first();

            // Email has not been found
            if (! $user) {
                return false;
            }

            // Email has not been validated
            if (! $user->valid_email) {
                return false;
            }

            return true;
        });
    }

    public function register()
    {
        //
    }
}

我是这样使用这个规则的:

public function rules()
{
    return [
        'email' => 'bail|required|checkEmailPresenceAndValidity'
    ];
}

但是,我想为每种情况设置不同的错误消息,如下所示:

if (! $user) {
    $WHATEVER_INST->error_message = 'email not found';
    return false;
}

if (! $user->valid_email) {
    $WHATEVER_INST->error_message = 'invalid email';
    return false;
}

但我不知道如果不做两个不同的规则如何实现这一点
当然,它可以处理多个规则,但它也将执行多个SQL查询,我真的希望避免这种情况
另外,请记住,在实际情况中,在一个规则中,我可以有两个以上的验证,比如这些验证。

有人有主意吗?

====
编辑1:

事实上,我想我想要的是一种类似于beteweensize规则的东西
它们表示一条规则,但提供多条错误消息:

'size'                 => [
    'numeric' => 'The :attribute must be :size.',
    'file'    => 'The :attribute must be :size kilobytes.',
    'string'  => 'The :attribute must be :size characters.',
    'array'   => 'The :attribute must contain :size items.',
],

Laravel检查该值是否表示数字、文件、字符串或数组;并获取正确的错误消息。
我们如何使用自定义规则实现这种事情?

共有3个答案

於德馨
2023-03-14

如果您使用的是Laravel 8,并且希望为特定验证显示不同的错误消息,请执行以下步骤。

我将创建一个验证规则,检查一个字段是有效的电子邮件还是有效的电话号码。它还会返回不同的错误消息。

创建一个自定义有效规则,如

php artisan make:rule EmailOrPhone

导航到规则文件夹ie Root中创建的文件-

粘贴以下代码

<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;

class EmailOrPhone implements Rule
{
    public $error_message;
    public function __construct()
    {
    }

    public function passes($attribute, $value)
    {

        $value = trim($value);
        if (is_numeric($value)){
            if (strlen($value) != 10){
                $this->error_message = "Phone number must contain 10 digits";
                return false;
            }else if (!Str::startsWith($value, '0')){
                $this->error_message = "Phone number must start with 0";
                return false;
            }else{
                return true;
            }
        }else{
            $validator = Validator::make(['email' => $value],[
                'email' => 'required|email'
            ]);

            if($validator->passes()){
               return true;
            }else{
                $this->error_message = "Please provide a valid email address";
                return false;
            }
        }
    }

    public function message()
    {
         return $this->error_message;
    }
}

现在可以在验证器中使用自定义验证,如

 return Validator::make($data, [
            'firstname' => ['required', 'string', 'max:255'],
            'lastname' => ['required', 'string', 'max:255'],
            'email_phone' => ['required', 'string', 'max:255', new EmailOrPhone()],
            'password' => ['required', 'string',  'confirmed'],
        ]);
琴镜
2023-03-14

对自定义验证规则处理不善是我放弃laravel的原因之一(好吧,这是许多原因之一,但可以说是压垮骆驼背的稻草)。但不管怎样,我有一个三部分的答案给你:一个你不想在这个具体案例中这样做的原因,一个你必须处理的混乱的简要概述,然后回答你的问题,以防你仍然想这样做。

重要的安全问题

管理登录的最佳安全实践规定,对于登录问题,您应该始终返回一条通用错误消息。典型的反例是,如果您为未找到的电子邮件返回“该电子邮件未在我们的系统中注册”,为使用错误密码的正确电子邮件返回“错误密码”。在您提供单独验证消息的情况下,您为潜在攻击者提供了有关如何更有效地指导其攻击的附加信息。因此,所有与登录相关的问题都应返回一条通用验证消息,而不管其根本原因是什么,该消息的效果是“无效的电子邮件/密码组合”。密码恢复表单也是如此,通常会说“密码恢复说明已发送到该电子邮件,如果它存在于我们的系统中”。否则,您会让攻击者(和其他人)知道在您的系统中注册了哪些电子邮件地址,这可能会暴露其他攻击向量。所以在这个特殊的例子中,一条验证消息就是您想要的。

laravel的麻烦

您遇到的问题是,laravel验证器只返回true或false来表示是否满足规则。错误消息单独处理。您特别不能从验证器逻辑内部指定验证器错误消息。我知道。这很可笑,而且计划得很糟糕。您所能做的就是返回true或false。您无法访问任何其他帮助您的内容,因此您的伪代码无法执行此操作。

(丑陋的)答案

创建自己的验证消息的最简单方法是创建自己的验证器。(在控制器内部)看起来是这样的:

$validator = Validator::make($input, $rules, $messages);

您仍然需要在引导时创建验证器(您的Valiator::Ex的调用。然后,您可以通过将它们传递给自定义验证器来指定$规则。最后,您可以指定您的消息。像这样的东西,整体(在您的控制器内):

public function login( Request $request )
{
    $rules = [
        'email' => 'bail|required|checkEmailPresenceAndValidity'
    ]

    $messages = [
        'checkEmailPresenceAndValidity' => 'Invalid email.',
    ];

    $validator = Validator::make($request->all(), $rules, $messages);
}

(我不记得是否必须在$messages数组中指定每个规则。我不这么认为)。当然,即使这样也不是很好,因为传递$messages的只是一个字符串数组(这就是允许的)。因此,您仍然无法根据用户输入轻松更改此错误消息。这一切都发生在验证程序运行之前。您的目标是根据验证结果更改验证消息,但是laravel强制您首先构建消息。因此,要真正做你想做的事情,你必须调整系统的实际流量,这不是很了不起。

解决方案是在控制器中有一个方法,用于计算是否满足自定义验证规则。它将在您创建验证器之前执行此操作,以便您可以向您构建的验证器发送适当的消息。然后,在创建验证规则时,还可以将其绑定到验证计算器的结果,只要将规则定义移动到控制器中即可。你只需要确保不要意外地把事情说得乱七八糟。您还必须记住,这需要将您的验证逻辑移到验证器之外,这相当麻烦。不幸的是,我95%肯定没有其他方法可以做到这一点。

下面是一些示例代码。它肯定有一些缺点:你的规则不再是全局的(它是在控制器中定义的),验证逻辑移出验证器(这违反了最小惊讶的原则),你将不得不提出一个对象内缓存方案(这并不难)以确保不会执行两次查询,因为验证逻辑会被调用两次。重申一下,这绝对是hacky,但我相当肯定,这是做你想做的Laravel的唯一方法。可能有更好的方法来组织这件事,但这至少应该让你知道你需要做什么。

<?php
namespace App\Http\Controllers;

use User;
use Validator;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class LoginController extends Controller
{
    public function __construct() {
        Validator::extend('checkEmailPresenceAndValidity', function ($attribute, $value, $parameters, $validator) {

            return $this->checkLogin( $value ) === true ? true : false;

        });
    }

    public function checkLogin( $email ) {
        $user = User::where('email', $email)->first();

        // Email has not been found
        if (! $user) {
            return 'not found';
        }

        // Email has not been validated
        if (! $user->valid_email) {
            return 'invalid';
        }

        return true;
    }

    public function login( Request $request ) {

        $rules = [
            'email' => 'bail|required|checkEmailPresenceAndValidity'
        ]

        $hasError = $this->checkLogin( $request->email );
        if ( $hasError === 'not found' )
            $message = "That email wasn't found";
        elseif ( $hasError === 'invalid' )
            $message = "That is an invalid email";
        else
            $message = "Something was wrong with your request";


        $messages = [
            'checkEmailPresenceAndValidity' => $message,
        ];

        $validator = Validator::make($request->all(), $rules, $messages);

        if ($validator->fails()) {
            // do something and redirect/exit
        }

        // process successful form here
    }
}

此外,值得注意的是,这个实现依赖于对闭包的$this支持,我相信这是在PHP 5.4中添加的。如果您使用的是旧版本的PHP,您必须使用use向闭包提供$this

编辑以咆哮

真正的原因是,laravel验证系统的设计非常精细。每个验证规则只应验证一件事。因此,不必更改给定验证器的验证消息,因此为什么$messages(当您构建自己的验证器时)只接受纯字符串。

一般来说,粒度是应用程序设计中的一件好事,也是正确实现可靠原则所努力追求的。然而,这个特殊的实现让我发疯。我的一般编程理念是,一个好的实现应该使最常见的用例变得非常简单,然后为不太常见的用例让路。在这种情况下,laravel的体系结构使最常见的用例变得简单,但不太常见的用例几乎不可能。我不同意这种权衡。我对Laravel的总体印象是,只要你需要用Laravel的方式做事,它就很有效,但如果你出于任何原因不得不跳出框框,它会积极地让你的生活更加困难。在您的情况下,最好的答案可能是直接返回到该框中,即创建两个验证器,即使这意味着进行冗余查询。对应用程序性能的实际影响可能一点也不重要,但要使laravel按您希望的方式运行,您的长期可维护性将受到相当大的影响。

颜永怡
2023-03-14

不幸的是,Laravel目前没有提供一种具体的方法来直接从属性参数数组添加和调用验证规则。但这并不排除基于TraitRequest用法的潜在友好解决方案。

例如,请在下面找到我的解决方案。

第一件事是等待表单被处理,以便用抽象类处理表单请求。您需要做的是获取当前的Validator实例,并防止它在出现任何相关错误时进行进一步的验证。否则,您将存储验证器实例并调用您稍后将创建的自定义用户验证规则函数:

<?php

namespace App\Custom\Validation;

use \Illuminate\Foundation\Http\FormRequest;

abstract class MyCustomFormRequest extends FormRequest
{
    /** @var \Illuminate\Support\Facades\Validator */
    protected $v = null;

    protected function getValidatorInstance()
    {
        return parent::getValidatorInstance()->after(function ($validator) {
            if ($validator->errors()->all()) {
                // Stop doing further validations
                return;
            }
            $this->v = $validator;
            $this->next();
        });
    }

    /**
     * Add custom post-validation rules
     */
    protected function next()
    {

    }
}

下一步是创建您的Trait,它将提供通过当前验证程序实例验证输入的方法,并处理要显示的正确错误消息:

<?php

namespace App\Custom\Validation;

trait CustomUserValidations
{
    protected function validateUserEmailValidity($emailField)
    {
        $email = $this->input($emailField);

        $user = \App\User::where('email', $email)->first();

        if (! $user) {
            return $this->v->errors()->add($emailField, 'Email not found');
        }
        if (! $user->valid_email) {
            return $this->v->errors()->add($emailField, 'Email not valid');
        }

        // MORE VALIDATION POSSIBLE HERE
        // YOU CAN ADD AS MORE AS YOU WANT
        // ...
    }
}

最后,不要忘记扩展您的MyCustomFormRequest。例如,在您的php artisan make:request CreateUserRequest之后,下面是一种简单的方法:

<?php

namespace App\Http\Requests;

use App\Custom\Validation\MyCustomFormRequest;
use App\Custom\Validation\CustomUserValidations;

class CreateUserRequest extends MyCustomFormRequest
{
    use CustomUserValidations;

    /**
     * Add custom post-validation rules
     */
    public function next()
    {
        $this->validateUserEmailValidity('email');
    }

    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'email' => 'bail|required|email|max:255|unique:users',
            'password' => 'bail|required',
            'name' => 'bail|required|max:255',
            'first_name' => 'bail|required|max:255',
        ];
    }
}

我希望你能在我的建议中找到自己的路。

 类似资料:
  • 在@vue/cli 4.1中。1应用程序我使用v-money和vee validate,我发现所需规则不适用于v-money,因为它始终具有“0”值。因此,我在这里编写自定义验证http://vee-validate.logaretm.com/v2/guide/custom-rules.html#using-习惯规则 在测试页面中插入此测试[ple]控制台中出现警告: 在浏览器中,我看到错误: 我

  • 本文向大家介绍thinkphp5.0自定义验证规则使用方法,包括了thinkphp5.0自定义验证规则使用方法的使用技巧和注意事项,需要的朋友参考一下 我们在用thinkphp5.0时候,经常要自定义验证规则,这个写法与tp以前的版本有所区别,小编今天带来大家一起来学习一下5.0下验证规则的使用方法。 在thinkphp5中定义$rule(验证规则)有两种方式 方式一: 方式二: 如果方式一自定义

  • 我在数据库上实现查询层,使用和Spring Boot项目在sql数据库上执行CRUD操作。在GraphQL模式中,我提到了一些必须的字段,当这些字段在查询中没有提到时,它会以默认格式返回状态代码的错误消息。 错误: 这是我的代码与层架构模式 控制器: 服务: 配置: BookDataFetcher: 上面的代码按预期工作,但我这里的问题是如何自定义错误消息?在错误消息中,我想提及状态,因为这是来自

  • 我正在使用一个名为verify.js的新插件来验证表单字段,在我开始尝试创建自己的自定义验证规则之前,所有的工作都很顺利。 这里是他们文档的链接,其中解释了如何创建自定义验证:http://verifyjs.com/#custom-rules 更有帮助的是插件作者发布在JSfiddle上的这个示例:http://jsfidle.net/jpillora/r4t84/1/I几乎精确地复制了这里的代码

  • 问题内容: 我想自定义弹簧验证错误 但是我做不到。要采取的步骤是什么? 问题答案: 该JSR 303的默认邮件插补算法,您可以通过提供捆绑命名的资源来定制信息。在类路径中创建一个文件,其中包含: 这将更改@Size约束的默认消息,因此您应该使用@Size约束而不是特定于Hibernate的@Length约束。 您可以更改特定约束实例的消息,而不是更改所有约束的默认消息。message在约束上设置属

  • 问题内容: 我阅读了有关jQuery验证程序的答复,其中概述了一种根据数据库中的值检查用户名的方法。 香港专业教育学院试图实现此方法,但无论从PHP文件返回什么,我总是得到消息,用户名已被使用。 这是自定义方法… 这是验证代码… 有没有一种特定的方式我应该从php返回消息。 谢谢 一个 问题答案: 您正在执行一个AJAX请求,因此:自定义验证器返回true或false时,验证已经完成。 您将需要使