【问题标题】:Adding custom validation errors to Laravel form向 Laravel 表单添加自定义验证错误
【发布时间】:2014-04-05 04:46:06
【问题描述】:

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

// 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();
}

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

我该怎么做?

【问题讨论】:

    标签: php forms validation laravel laravel-4


    【解决方案1】:

    我一直在寻找实现这一目标的时间,我了解到@JustAMartin 所说的是真的。使用 Laravel 标准验证器实际上是不可能的,但要快速解决此问题,请通过您的脚本制作您自己的布尔切换器

    使用您喜欢的默认值启动它,然后检查您的验证,然后将您的错误存储在 Laravel 验证器中,因为这仍然有效,因此您可以完美地使用它来存储错误, 然后最后检查您的布尔值 (true|false),然后像往常一样成功或抛出所有错误,这里有一个我如何做的例子。

    我的情况是,如果他们准确地写了“确认删除”,我想检查一个文本字段,然后我想检查他们提供的密码是否正确以删除他们的帐户,你去:

    我用作解决方法的布尔值是 $eventSuccess

    public function accountDeletion(Request $request)
    {
        $eventSuccess = false;
        $validator = Validator::make($request->all(), [
            'textConfirm' => 'string|in:Confirm deletion'
        ]);
        if (!$validator->fails()) {
            $eventSuccess = true;
        }
    
        $user = Auth::user();
        if (Auth::validate(array('username' => $user->username, 'password' => $request->get('password')))) {
            $eventSuccess = false;
            $validator->messages()->add('password', 'Wrong password.');
        }
    
        if ($eventSuccess) {
            $user->delete();
            return true;
        }
        return $validator->messages()->get('*');
    }
    

    这对我有用,希望有人可以使用它。

    最好的问候,
    卡斯珀汤姆森

    【讨论】:

      【解决方案2】:

      用户 Matt K 在评论中说laravel has since implemented validation hooks,这正是我们想要的:

      $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!
      }
      

      【讨论】:

      • 或者你可以调用 $validator->validate()
      【解决方案3】:
       $validator -> errors() -> add('attribute', 'value');
       return redirect($request -> url())
                          -> withErrors($validator)
                          -> withInput();
      

      在“值”中你可以传递任何东西。

      【讨论】:

        【解决方案4】:

        如果您使用 ajax 调用,请不要忘记抛出 ValidationException

        if ($subscribed) {
            $validator->errors()->add('email', __('Your email is already subscribed.'));
            throw new ValidationException($validator);
        }
        

        【讨论】:

          【解决方案5】:

          我通过验证和自定义验证解决了类似的问题。就我而言,我需要验证带有表单的上传文件是有效图像以及发布数据,因此我需要对文件运行验证测试并为发布数据运行验证测试。当我试图返回我的自定义验证数据时,我遇到了问题,只存在 Laravel 的验证错误。根据@JustAMartin 的帖子,我编写了一个显示所有错误的解决方案。

              //Creem una instància del validador. Açò ens permet manipular-lo
              $validator = Validator::make($request->all(), [
                  'nomCompanyia' => 'required',
                  'urlCompanyia' => 'url'
              ]);
          
              $imageError = false;
              $imgOriginal = null;
              $imgMitjana = null;
              $imgXicoteta = null;
              $fallaValidacio = !$validator->passes(); //-> Retorna true si cap error, false en cas contrari.
          
              if($request->hasFile('logoCompanyia') && !$fallaValidacio)
              {
                  $imatge = $request->file('logoCompanyia');
          
                  if($imatge->isValid() && $this->verificaExtensionsImatges($imatge->getClientOriginalExtension(), $imatge->guessExtension()))
                  {
                      $sPath = $imatge->store('images/companyies/', 'public');
                      $fullPathOriginal = public_path() . "/storage/" . $sPath;
                      $fpInfo = pathinfo($fullPathOriginal);
                      $imgOriginal = sprintf("%s.%s", $fpInfo['filename'], $fpInfo['extension']);
          
                      //Crear les miniatures
                      $mitjana = Image::make($fullPathOriginal)->widen(300, function ($constraint) {
                          $constraint->upsize();
                      });
          
                      $imgMitjana = sprintf("%s_300.%s", $fpInfo['filename'], $fpInfo['extension']);
                      $mitjana->save($fpInfo['dirname'] . '/' . $imgMitjana);
          
                      $xicoteta = Image::make($fullPathOriginal)->widen(100, function ($constraint) {
                          $constraint->upsize();
                      });
          
                      $imgXicoteta = sprintf("%s_100.%s", $fpInfo['filename'], $fpInfo['extension']);
                      $xicoteta->save($fpInfo['dirname'] . '/' . $imgXicoteta);
                  }
                  else
                  {
                      $imageError = true;
                      $validator->getMessageBag()->add('logoCompanyia', "Sembla que el fitxer d'imatge no és vàlid o està corrupte. Només s'accepten els formats: .jpg, .jpeg, .png, .gif");
                  }
              }
              else
              {
                  $imageError = true;
                  $validator->getMessageBag()->add('logoCompanyia', "Sembla que el fitxer d'imatge no és vàlid o ha sigut rebutjat per el servidor si és massa gran.");
              }
          
              if($fallaValidacio || $imageError)
              {
                  $data['mode'] = "nou";
                  $data['urlFormulari'] = "administracio/companyies/afegir";
                  $data['nomCompanyia'] = $request->nomCompanyia;
                  $data['idCompanyia'] = 0;
                  $data['urlCompanyia'] = $request->urlCompanyia;
                  $data['logoCompanyia'] = $request->logoCompanyia;
                  $data['errors'] = (object) $validator->errors();
          
                  return view($this->formulariTemplate, $data);
              }
          
              $companyia = new Companyies();
              $companyia->nom = $request->nomCompanyia;
              $companyia->url = $request->urlCompanyia;
              $companyia->logo_original = $imgOriginal;
              $companyia->logo_300 = $imgMitjana;
              $companyia->logo_100 = $imgXicoteta;
          
              $companyia->save();
          

          如您所见,我只调用了一次 $validator->passes() 并将结果存储在一个变量中。当我调用这个方法时,所有的 Laravel 测试都完成了。如果它们通过或没有结果存储在变量中,那么您可以稍后测试您的变量。这允许对文件进行测试以最终确定所有数据是否正常。

          如果有错误,我会使用 view() 帮助程序重定向回来,添加所有数据:输入和错误。如果没有错误,则继续该方法的正常行为。

          【讨论】:

            【解决方案6】:

            接受的答案存在一个问题(在我看来,通常是 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 的 Validator 代码,你会发现以下内容:

            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
            

            如果你不喜欢滥用 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;
            }
            

            我可以这样使用它:

                $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
            

            当然,这仍然是解决该问题的一种老套方法。

            理想情况下,我会重新设计验证器,以清楚地将其验证阶段与状态检测分开(validate()passes() 或更好的isValid() 的单独方法),并添加方便的方法来手动使特定字段与特定规则失败。虽然这也可能被认为是 hacky,但如果我们想使用 Laravel 验证器,不仅要使用 Laravel 自己的验证规则,还要使用我们自定义的业务逻辑规则。

            【讨论】:

            • “没有办法将错误附加到现有验证器并使其失败”......有!您可以使用所谓的“after validation hook”。在提出原始问题时它不可用,但自 Larvel 5.3 以来它就已经存在。
            【解决方案7】:

            替代语法:

            $validator->errors()
                      ->add('photos', 'At least one photo is required for a new listing.');
            

            【讨论】:

              【解决方案8】:

              此外,添加以下Redirect::back() 函数可能会有所帮助:

              $validator->getMessageBag()->add('password', 'Password wrong');    
              return Redirect::back()->withErrors($validator)->withInput();
              

              根据

              阿尔法

              (http://heera.it/laravel-manually-invalidate-validation#.VVt7Wfl_NBc)

              【讨论】:

                【解决方案9】:

                请参阅达伦·克雷格的回答。

                一种实现方式。

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

                【讨论】:

                • 不知道为什么我需要检查电子邮件,但消息包的东西可以用,谢谢。 :)
                • 这很酷。我仍然不会倾向于将我的验证错误和应用程序消息混为一谈。
                • 请注意 - 如果您手动附加错误,验证器本身不会处于失败状态。因此,如果您稍后调用`$validator->fails()`,它将返回false 而不是true。如果您需要添加自定义错误并使验证器失败,请参阅下面的答案以了解为什么会发生这种情况以及可以做什么。
                • 从那时起,在 laravel 中实现了一个更好的答案,validation hooks - 也请参阅下面的答案
                【解决方案10】:

                我理解您为什么要这样做,但是从安全的角度来看,返回指示用户名和/或密码是否不正确的消息实际上是一种不好的做法。它可以让黑客了解他们的用户名或密码是否正确。

                最好返回诸如“您的凭据不正确”之类的通用消息,无论如何您都不希望显示在您的字段旁边。

                【讨论】:

                • 这是在他们帐户设置中的更改电子邮件页面上。如果他们达到这一点,他们已经拥有用户的凭据。
                猜你喜欢
                • 2020-06-02
                • 2017-05-30
                • 2012-05-07
                • 1970-01-01
                • 2012-09-16
                • 2015-08-13
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多