Как получить все возможные сообщения об ошибках метода Laravel Controller - PullRequest
4 голосов
/ 10 октября 2019

Итак, у меня есть приложение 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 ]

3 голосов
/ 16 октября 2019

Для этого вам необходимо расширить класс 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());
  }

}
array:13 [▼
  0 => "The id field is required."
  1 => "The id must be an integer."
  2 => "The id must be between 2 and 10."
  3 => "The status field is required when nonexisting is present."
  4 => "The status must be a string."
  5 => "The status must be a valid email address."
  6 => "The avatar failed to upload."
  7 => "The avatar field is required."
  8 => "The avatar must be a file."
  9 => "The avatar must be a file of type: png."
  10 => "The avatar may not be greater than 1000 kilobytes."
  11 => "The company field is required when id is not present."
  12 => "The company has already been taken."
]
1 голос
/ 20 октября 2019

Номер 1: Как я уже упоминал в своем комментарии под вопросом, то, что вы пытаетесь достичь, может быть сделано более простым способом.

Номер 2: Поскольку вы не хотите менять уже написанный код, где вы получили ->messages(), вы можете сделать следующее. Я перечислю шаги и приведу пример кода.

  • Нам нужно переопределить классы валидатора Laravel, фабрики (Validation) и провайдера ValidationService.
  • В папке App\Services вы можетесоздайте два класса Validator и ValidationFactory
  • в 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, или если все правила возвращают правильные сообщения.

Обычно вы хотите возвращать пользователю сообщения с ошибками, а не все возможные сбои.

0 голосов
/ 22 октября 2019

На основе процедуры проверки формы 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 () метод возвращает все ошибки в виде ассоциативного массива, в котором будет находиться сообщениеассоциируйте с именем поля соответственно, и вот как вы можете получить ошибки.

0 голосов
/ 17 октября 2019

Это не красиво, но вот мой снимок:

$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);
}
0 голосов
/ 15 октября 2019

Я думаю, что вы ищете способ иметь пользовательские сообщения об ошибках. если это так, то ответ будет таким:

$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);

больше информации вы можете найти здесь .

Я надеюсь, что это то, что вы ищете, имой ответ тебе помогает:)

0 голосов
/ 10 октября 2019

Я думаю, что функции failed() (получить ошибочные правила проверки) или errors() (получить контейнер сообщений для валидатора) могут вам помочь. Если это не так - перейдите на https://laravel.com/api/5.8/Illuminate/Validation/Validator.html и я надеюсь, что вы найдете нужную функцию.

0 голосов
/ 10 октября 2019

Я думаю, вам следует реализовать https://laravel.com/docs/5.8/validation#form-request-validation.

Это поможет вам в реализации ставок.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...