web-dev-qa-db-ja.com

カスタム検証エラーを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入力にフォームエラーを追加して、通常のフォーム検証と同じように表示したいのです。そのような何か:

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

そうすれば、不正なパスワードエラーがパスワード入力の横に表示され、適切なフォーム検証のように見えます。

これどうやってするの?

18
John Dorean

ダレンクレイグの回答を参照してください。

ただし、それを実装する1つの方法。

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

受け入れられた回答(および私の意見ではLaravelの一般的なバリデーター)には1つの問題があります-検証プロセス自体と検証ステータスの検出は1つのメソッドにマージされます。

バッグからすべての検証メッセージを盲目的にレンダリングする場合、それは大したことではありません。ただし、バリデーターが失敗したかどうかを検出し、追加のアクション(現在の検証済みフォームフィールドに国際テキストメッセージを送るなど)を実行する追加のロジックがある場合は、問題があります。

デモンストレーション:

_    // 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カスタムルールでバリデーターを拡張できます。一般的な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バリデーターをLaravel独自の検証ルールだけでなく、カスタムビジネスロジックルールでも使用する場合は、他に選択肢はありません。

12
JustAMartin

代替構文:

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

さらに、次のRedirect::back()関数を追加すると役立つ場合があります。

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

による

アルファ

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

3
Pathros

ユーザーMatt Kがコメントで言った laravelは検証フックを実装して以来 、これはまさに私たちが望むことを行う:

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

検証とカスタム検証で同様の問題を解決しました。私の場合、フォームを含むアップロードされたファイルが有効な画像と投稿データであることを確認する必要があるため、ファイルの検証テストと投稿データの検証テストを実行する必要があります。カスタム検証データを返そうとしたときに問題が発生しましたが、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()を1回呼び出すだけで、結果を変数に格納しています。このメソッドを呼び出すと、すべてのLaravelテストが作成されます。テストが渡されるかどうかが結果に変数に格納されるので、後で変数をテストできます。これにより、すべてのデータに問題がないかどうかを最終的に判断するファイル。

エラーがある場合は、view()ヘルパーを使用してリダイレクトし、すべてのデータ(入力とエラー)を追加します。エラーがない場合、メソッドの通常の動作が続行されます。

0
 $validator -> errors() -> add('attribute', 'value');
 return redirect($request -> url())
                    -> withErrors($validator)
                    -> withInput();

「値」には何でも渡すことができます。

0
Lipa

Ajax呼び出しを使用している場合は、ValidationExceptionをスローすることを忘れないでください。

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

なぜこれが必要なのか理解しましたが、セキュリティの観点からは、ユーザー名やパスワードが間違っているかどうかを示すメッセージを返すのは実際には悪い習慣です。これにより、ハッカーはユーザー名またはパスワードが正しいかどうかを理解できます。

「資格情報が正しくありません」などの一般的なメッセージを返すことをお勧めします。これはとにかくフィールドの横に表示する必要はありません。

0
Darren Craig