如何获取Laravel控制器方法的所有可能的错误消息

时间:2019-10-10 07:16:06

标签: php laravel laravel-5 error-messaging

所以我有一个Laravel应用程序,其中有许多控制器可以处理应用程序的各个方面。

现在每个控制器都有各种方法。大多数方法都定义了验证规则,例如:

$validationArray = [
    'id'=>'required|integer',
    'status'=>'required|string'
];
$validator = Validator::make($request->all(),$validationArray);
if ($validator->fails()){
    return Response::json(['response'=>implode(', ',$validator->messages()->all())],422);
}

现在以下行:

return Response::json(['response'=>implode(', ',$validator->messages()->all())],422);

实际上会返回验证规则的任何错误。

我的问题是:有没有办法以编程方式获取所有可能的错误消息?

当然,一种方法是按规则进行操作并手动创建列表,但是有数百种方法分散在各个控制器上。

因此,如果有人能指出我以更轻松的方式处理所有错误消息的方向,将不胜感激。

提前谢谢!

更新

因此,为了进一步清除,我需要列出所有可能的错误,例如上面的代码,该列表将是:

['id is required', 'id must be an integer', 'status is required', 'status must be an string']

更新2

请记住,有数百种方法,而且我不想更改该方法的最终响应,而是希望拥有某种外部脚本,这些脚本可以帮助我获取错误消息而又不会过多地干扰控制器。

7 个答案:

答案 0 :(得分:3)

为此,您必须扩展sklearn类并编写一个将迭代所有规则并显式添加错误消息的方法,就像它们失败一样。

首先,创建一个新文件Validator

app\Http\Custom\Validator.php

接下来,让我们在验证工厂中注册此类:

<?php

namespace App\Http\Custom;

use Illuminate\Contracts\Validation\Rule as RuleContract;
use Illuminate\Support\MessageBag;
use Illuminate\Validation\ValidationRuleParser;
use Illuminate\Validation\Validator as BaseValidator;

class Validator extends BaseValidator {

  /** @var MessageBag */
  protected $errorMessages;

  /** @var array */
  protected $hasExplicitFileErrorMessage;

  protected $explicitFileRules = [
    'File', 'Image', 'Mimes', 'Mimetypes', 'Dimensions',
  ];

  function availableErrors()
  {
    $this->errorMessages = new MessageBag();
    $this->hasExplicitFileErrorMessage = [];

    foreach($this->rules as $attribute => $rules) {
      $attribute = str_replace('\.', '->', $attribute);
      foreach($rules as $rule) {
        [$rule, $parameters] = ValidationRuleParser::parse($rule);

        if($rule == '') {
          continue;
        }
        if(($keys = $this->getExplicitKeys($attribute)) &&
          $this->dependsOnOtherFields($rule)) {
          $parameters = $this->replaceAsterisksInParameters($parameters, $keys);
        }
        // explicitly add "failed to upload" error
        if($this->hasRule($attribute, $this->explicitFileRules) && !in_array($attribute, $this->hasExplicitFileErrorMessage)) {
          $this->addFailureMessage($attribute, 'uploaded', []);
          $this->hasExplicitFileErrorMessage[] = $attribute;
        }

        if($rule instanceof RuleContract) {
          $messages = $rule->message() ? (array)$rule->message() : [get_class($rule)];
          foreach($messages as $message) {
            $this->addFailureMessage($attribute, get_class($rule), [], $message);
          }
        } else {
          $this->addFailureMessage($attribute, $rule, $parameters);
        }
      }
    }

    return $this->errorMessages->all();
  }

  function addFailureMessage($attribute, $rule, $parameters = [], $rawMessage = null)
  {
    $this->errorMessages->add($attribute, $this->makeReplacements(
      $rawMessage ?? $this->getMessage($attribute, $rule), $attribute, $rule, $parameters
    ));
  }

  // we have to override this method since file-type errors depends on data value rather than rule type
  protected function getAttributeType($attribute)
  {
    if($this->hasRule($attribute, $this->explicitFileRules)) {
      return 'file';
    }
    return parent::getAttributeType($attribute);
  }
}

然后...仅此而已。让我们测试一下:

<?php

namespace App\Providers;

use App\Http\Custom\Validator; // <-- our custom validator
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider {

  public function boot()
  {
    app('validator')->resolver(function ($translator, $data, $rules, $messages) {
      return new Validator($translator, $data, $rules, $messages);
    });
  }

}
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

class HomeController extends Controller {

  function index(Request $request)
  {
    $rules = [
      'id'      => 'required|int|between:2,10',
      'status'  => 'required_with:nonexisting|string|email',
      'avatar'  => 'required|file|mimes:png|max:1000',
      'company' => 'required_without:id|unique:companies,id'
    ];

    $validator = Validator::make([], $rules);

    dump($validator->availableErrors());
  }

}

答案 1 :(得分:1)

虽然不漂亮,但这是我的镜头:

$validationArray = [
    'id'=>'required|integer',
    'status'=>'required|string'
];

$validator = Validator::make($request->all(), $validationArray);
if ($validator->fails()) {
    $messages = [];
    $invalid_fields = array_keys($validator->messages()->toArray());
    $rules = $v->getRules();

    foreach($invalid_fields as $invalid_field) {
        foreach($rules[$invalid_field] as $rule) {
            if(str_contains($rule, ':') {
                // complex rules that have parameters (min, between, size, format) 
                // are more difficult to work with. I haven't figured out how to do them yet
                // but you should get the idea. 
                continue;
            } else {
                $messages[] = str_replace(':attribute', $invalid_field, $validator->getTranslator()->get("validation.$rule"));
            }
        }
    }

    return Response::json(['response' => implode(', ', $messages)], 422);
}

答案 2 :(得分:1)

数字1:就像我在问题下的评论中提到的那样,您要实现的目标可以通过更简单的方式完成。

数字2:由于您不想更改已获得->messages()的已编写代码,因此可以执行以下操作。我将列出这些步骤并提供示例代码。

  • 我们需要重写Laravel的验证器,(Validation)工厂和ValidationService提供程序类。
  • App\Services文件夹中,您可以创建两个类ValidatorValidationFactory
  • App\Providers中创建一个类ValidationServiceProvider
  • 进入config/app.php文件,在providers下将Illuminate\Validation\ValidationServiceProvider::class替换为App\Providers\ValidationServiceProvider::class
  

Validator类看起来像这样:

namespace App\Services;

use Illuminate\Support\MessageBag;
use Illuminate\Validation\ValidationRuleParser;
use Illuminate\Contracts\Translation\Translator;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Illuminate\Contracts\Validation\Rule as RuleContract;

class Validator extends \Illuminate\Validation\Validator
{
    /**
     * @var MessageBag $all_messages
     */
    protected $all_messages;

    public function __construct(Translator $translator, array $data, array $rules, array $messages = [], array $customAttributes = [])
    {
        parent::__construct($translator, $data, $rules, $messages, $customAttributes);

        $this->all_messages = new MessageBag;
        $this->getAllFormattedMessages();
    }

    public function makeAllRulesMessages($attribute, $rule, $parameters)
    {
        $this->all_messages->add($attribute, $this->makeReplacements(
            $this->getMessage($attribute, $rule), $attribute, $rule, $parameters
        ));
    }

    public function messages(bool $validated_rules_messages = false)
    {
        return $validated_rules_messages
            ? $this->validatedMessages()
            : $this->all_messages;
    }

    /**
     * This is here in case the true validated messages are needed
     *
     * @return MessageBag
     */
    public function validatedMessages()
    {
        return parent::messages();
    }

    public function getAllFormattedMessages()
    {
        // We'll spin through each rule and add all messages to it.
        foreach ($this->rules as $attribute => $rules) {
            $attribute = str_replace('\.', '->', $attribute);

            foreach ($rules as $rule) {
                // First we will get the correct keys for the given attribute in case the field is nested in
                // an array. Then we determine if the given rule accepts other field names as parameters.
                // If so, we will replace any asterisks found in the parameters with the correct keys.

                [$rule, $parameters] = ValidationRuleParser::parse($rule);

                if (($keys = $this->getExplicitKeys($attribute)) &&
                    $this->dependsOnOtherFields($rule)) {
                    $parameters = $this->replaceAsterisksInParameters($parameters, $keys);
                }

                $value = $this->getValue($attribute);

                if ($value instanceof UploadedFile && $this->hasRule($attribute, array_merge($this->fileRules, $this->implicitRules))
                ) {
                    $this->makeAllRulesMessages($attribute, 'uploaded', []);
                } elseif ($rule instanceof RuleContract) {
                     $this->makeCustomRuleMessage($attribute, $rule);
                } else {
                    $this->makeAllRulesMessages($attribute, $rule, $parameters);
                }

            }
        }
    }

    /**
     * @param $attribute
     * @param \Illuminate\Contracts\Validation\Rule  $rule $rule
     */
    public function makeCustomRuleMessage($attribute, $rule)
    {
        $this->failedRules[$attribute][get_class($rule)] = [];

        $messages = (array)$rule->message();

        foreach ($messages as $message) {
            $this->all_messages->add($attribute, $this->makeReplacements(
                $message, $attribute, get_class($rule), []
            ));
        }
    }
}

该类概括地做一件事,将传递的规则的所有消息放入该类的$all_messages属性中。它扩展并允许基本验证类运行,并且仅覆盖messages()方法以使所有收集的规则可供使用。

  

ValidationFactory覆盖Illuminate\Validation\Factory,看起来像这样:

namespace App\Services;

use Illuminate\Validation\Factory;

class ValidationFactory extends Factory
{
    /**
     * Resolve a new Validator instance.
     *
     * @param  array  $data
     * @param  array  $rules
     * @param  array  $messages
     * @param  array  $customAttributes
     * @return \Illuminate\Validation\Validator
     */
    protected function resolve(array $data, array $rules, array $messages, array $customAttributes)
    {
        if (is_null($this->resolver)) {
            return new \App\Services\Validator($this->translator, $data, $rules, $messages, $customAttributes);
        }

        return call_user_func($this->resolver, $this->translator, $data, $rules, $messages, $customAttributes);
    }
}

此类仅做一件事,而是通过使用自定义resolve()类的实例来覆盖此类中的\App\Services\Validator方法。

  

ValidationServiceProvider扩展了Illuminate\Validation\ValidationServiceProvider并覆盖了registerValidationFactory()方法,看起来像这样:

namespace App\Providers;

use App\Services\ValidationFactory;
use Illuminate\Validation\ValidationServiceProvider as BaseValidationServiceProvider;

class ValidationServiceProvider extends BaseValidationServiceProvider
{

    protected function registerValidationFactory()
    {
        $this->app->singleton('validator', function ($app) {
            $validator = new ValidationFactory($app['translator'], $app);

            // The validation presence verifier is responsible for determining the existence of
            // values in a given data collection which is typically a relational database or
            // other persistent data stores. It is used to check for "uniqueness" as well.
            if (isset($app['db'], $app['validation.presence'])) {
                $validator->setPresenceVerifier($app['validation.presence']);
            }

            return $validator;
        });
    }
}

以上类的作用还在于指示提供者在应用需要时使用我们的App\Services\ValidationFactory

我们完成了。即使我们的验证规则之一失败,也会显示所有验证消息。

注意事项

  

为了实现这一目标,我们需要进行很多更改和替代。除了非常关键之外,这可能表示该应用程序的设计看起来有问题。

     

Laravel验证实现可能会在将来的版本中更改,因此可能会成为维护这些更改的问题。

     

我无法判断是否有其他副作用可能会覆盖Laravel的默认验证实现,或者所有规则是否返回正确的消息。

     

通常,您只想将失败的验证消息返回给用户,而不是所有可能的失败。

答案 3 :(得分:0)

我认为,您应该实现https://laravel.com/docs/5.8/validation#form-request-validation

这将帮助您更好地实施投注。

答案 4 :(得分:0)

我认为函数failed()(获取失败的验证规则)或errors()(获取验证者的消息容器)可能会对您有所帮助。如果没有,请转到apply_async,希望您找到所需的功能。

答案 5 :(得分:0)

我认为您正在寻找一种具有自定义错误消息的方法。如果是这种情况,那么答案是这样的:

$messages = [
    'id.required' => 'id is required',
    'id.integer' => 'id must be an integer',
    'status.required' => 'status is required',
    'status.string'=> 'status must be an string'
];

$validationArray = [
    'id'=>'required|integer',
    'status'=>'required|string'
];

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

更多信息,您可以找到here

我希望这是您正在寻找的东西,我的回答会为您提供帮助:)

答案 6 :(得分:0)

根据Laravel表单验证程序,您可以通过以下方式编写语句:

 $validationArray = [
   'id'=>'required|integer',
   'status'=>'required|string'
 ];

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

 if ($validator->fails()){
  return Response::json(['response'=> validator->errors())],422);
 }

errors()方法将所有错误作为关联数组返回,在该数组中,消息将与字段名称相应地关联,这就是获取错误的方式。