Добавление пользовательских ошибок проверки в форму Laravel

у меня есть базовая форма, настроенная, чтобы позволить пользователю изменить свой адрес электронной почты, и я делаю следующую проверку на нем, прежде чем изменить электронную почту:

// 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 input, поэтому он отображается так же, как обычная проверка формы. Что-то вроде этого:

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

таким образом, неправильная ошибка пароля будет отображаться рядом с моим вводом пароля и выглядеть как правильная проверка формы.

как я могу это сделать?

6 ответов


см. ответ Даррена Крейга.

один из способов его реализации.

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

существует одна проблема с принятым ответом (и валидатором 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, вы найдете следующее:

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


кроме того, было бы полезно добавить следующее Redirect::back() функция:

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

по данным

Альфа

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


альтернативный синтаксис:

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

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

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


Я решил аналогичную проблему с проверкой и пользовательской проверкой. В моем случае мне нужно проверить, что загруженный файл с формой является допустимым изображением, а также данными post, поэтому мне нужно запустить тест проверки для файла и тесты проверки для данных post. Я был проблемой, когда пытался вернуть свои пользовательские данные проверки, присутствовали только ошибки проверки 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 (), добавляя все данные: ввод и ошибки. Если нет ошибки нормальное поведение для метода продолжается.