web-dev-qa-db-ja.com

Symfony2:リクエストをフォームにバインドした後にフォーム検証エラーを取得する方法

これがsaveActionコードです(フォームがデータを渡す場所)

public function saveAction()
{
    $user = OBUser();

    $form = $this->createForm(new OBUserType(), $user);

    if ($this->request->getMethod() == 'POST')
    {
        $form->bindRequest($this->request);
        if ($form->isValid())
            return $this->redirect($this->generateUrl('success_page'));
        else
            return $this->redirect($this->generateUrl('registration_form'));
    } else
        return new Response();
}

私の質問は:$form->isValid()falseを返す場合、どのようにエラーを取得しますか?

108
putolaruan

次の2つの方法があります。

  • エラー時にユーザーをリダイレクトせず、テンプレートファイル内で{{ form_errors(form) }}を表示します
  • $form->getErrors()としてエラー配列にアクセスします
116
nefo_x

Symfony 2.3/2.4:

この関数はすべてのエラーを取得します。 「CSRFトークンが無効です。フォームを再送信してください。」などのフォーム上のもの同様に、エラーのバブリングを持たないフォームの子に関する追加エラーもあります。

private function getErrorMessages(\Symfony\Component\Form\Form $form) {
    $errors = array();

    foreach ($form->getErrors() as $key => $error) {
        if ($form->isRoot()) {
            $errors['#'][] = $error->getMessage();
        } else {
            $errors[] = $error->getMessage();
        }
    }

    foreach ($form->all() as $child) {
        if (!$child->isValid()) {
            $errors[$child->getName()] = $this->getErrorMessages($child);
        }
    }

    return $errors;
}

すべてのエラーを文字列として取得するには:

$string = var_export($this->getErrorMessages($form), true);

Symfony 2.5/3.0:

$string = (string) $form->getErrors(true, false);

ドキュメント:
https://github.com/symfony/symfony/blob/master/UPGRADE-2.5.md#formhttps://github.com/symfony/symfony /blob/master/UPGRADE-3.0.md#form (下部:The method Form::getErrorsAsString() was removed

98
Flip

以下は私のために働いた解決策です。この関数はコントローラー内にあり、すべてのエラーメッセージとそれらの原因となったフィールドの構造化された配列を返します。

Symfony 2.0:

private function getErrorMessages(\Symfony\Component\Form\Form $form) {
    $errors = array();
    foreach ($form->getErrors() as $key => $error) {
        $template = $error->getMessageTemplate();
        $parameters = $error->getMessageParameters();

        foreach($parameters as $var => $value){
            $template = str_replace($var, $value, $template);
        }

        $errors[$key] = $template;
    }
    if ($form->hasChildren()) {
        foreach ($form->getChildren() as $child) {
            if (!$child->isValid()) {
                $errors[$child->getName()] = $this->getErrorMessages($child);
            }
        }
    }

    return $errors;
}

Symfony 2.1以降:

private function getErrorMessages(\Symfony\Component\Form\Form $form) {      
    $errors = array();

    if ($form->hasChildren()) {
        foreach ($form->getChildren() as $child) {
            if (!$child->isValid()) {
                $errors[$child->getName()] = $this->getErrorMessages($child);
            }
        }
    } else {
        foreach ($form->getErrors() as $key => $error) {
            $errors[] = $error->getMessage();
        }   
    }

    return $errors;
}
47
Icode4food

バリデーターを使用して特定のエンティティのエラーを取得します

if( $form->isValid() )
{
    // ...
}
else
{
    // get a ConstraintViolationList
    $errors = $this->get('validator')->validate( $user );

    $result = '';

    // iterate on it
    foreach( $errors as $error )
    {
        // Do stuff with:
        //   $error->getPropertyPath() : the field that caused the error
        //   $error->getMessage() : the error message
    }
}

APIリファレンス:

現在SF 2.6.3を使用している適切な(翻訳可能な)メッセージを取得するために、ここに私の最終機能があります(上記のものはもう動作しないようです)。

 private function getErrorMessages(\Symfony\Component\Form\Form $form) {      
    $errors = array();
    foreach ($form->getErrors(true, false) as $error) {
        // My personnal need was to get translatable messages
        // $errors[] = $this->trans($error->current()->getMessage());
        $errors[] = $error->current()->getMessage();
    }

    return $errors;
}

form :: getErrors()メソッドがFormErrorIteratorのインスタンスを返すようになりました。ただし、2番目の引数($ flatten)をtrue。 (FormErrorインスタンスを返すため、current()メソッドを使用せずにgetMessage()メソッドを直接呼び出す必要があります。

 private function getErrorMessages(\Symfony\Component\Form\Form $form) {      
    $errors = array();
    foreach ($form->getErrors(true, true) as $error) {
        // My personnal need was to get translatable messages
        // $errors[] = $this->trans($error->getMessage());
        $errors[] = $error->getMessage();
    }

    return $errors;
}

最も重要なことは、実際にはエラーを取得するために最初の引数をtrueに設定することです。 2番目の引数($ flatten)をデフォルト値(true)のままにすると、FormErrorインスタンスが返されますが、は返されますfalseに設定された場合のFormErrorIteratorインスタンス。

19
Cedo

廃止予定の関数のないsymfony 2.1以降の関数:

/**
 * @param \Symfony\Component\Form\Form $form
 *
 * @return array
 */
private function getErrorMessages(\Symfony\Component\Form\Form $form)
{
    $errors = array();

    if ($form->count() > 0) {
        foreach ($form->all() as $child) {
            /**
             * @var \Symfony\Component\Form\Form $child
             */
            if (!$child->isValid()) {
                $errors[$child->getName()] = $this->getErrorMessages($child);
            }
        }
    } else {
        /**
         * @var \Symfony\Component\Form\FormError $error
         */
        foreach ($form->getErrors() as $key => $error) {
            $errors[] = $error->getMessage();
        }
    }

    return $errors;
}
15
stwe

フラッシュメッセージについては、$form->getErrorsAsString()に満足しました

編集(Benji_X80から):SF3には$form->getErrors(true, false);を使用します

15
Tjorriemorrie

翻訳されたフォームのエラーメッセージ(Symfony2.1)

私はこの情報を見つけるのに苦労してきたので、フォームエラーの翻訳に関するメモを追加することは間違いなく価値があると思います。

@Icode4food answerは、フォームのすべてのエラーを返します。ただし、返される配列は、メッセージの複数形化translationも考慮しません。

@Icode4food answerのforeachループを変更して、コンボを作成できます。

  • 特定のフォームのすべてのエラーを取得する
  • 翻訳されたエラーを返す
  • 必要に応じて複数形を考慮する

ここにあります:

foreach ($form->getErrors() as $key => $error) {

   //If the message requires pluralization
    if($error->getMessagePluralization() !== null) {
        $errors[] = $this->container->get('translator')->transChoice(
            $error->getMessage(), 
            $error->getMessagePluralization(), 
            $error->getMessageParameters(), 
            'validators'
            );
    } 
    //Otherwise, we do a classic translation
    else {
        $errors[] = $this->container->get('translator')->trans(
            $error->getMessage(), 
            array(), 
            'validators'
            );
    }
}

この回答は、3つの異なる投稿からまとめられています。

4
Mick

翻訳されたフォームのエラーメッセージ(Symfony2.3)

問題を解決する私のバージョン:

/src/Acme/MyBundle/Resources/config/services.yml

services:
    form_errors:
        class: Acme\MyBundle\Form\FormErrors

/src/Acme/MyBundle/Form/FormErrors.php

<?php
namespace Acme\MyBundle\Form;

class FormErrors
{
    public function getArray(\Symfony\Component\Form\Form $form)
    {
        return $this->getErrors($form);
    }

    private function getErrors($form)
    {
        $errors = array();

        if ($form instanceof \Symfony\Component\Form\Form) {

            // соберем ошибки элемента
            foreach ($form->getErrors() as $error) {

                $errors[] = $error->getMessage();
            }

            // пробежимся под дочерним элементам
            foreach ($form->all() as $key => $child) {
                /** @var $child \Symfony\Component\Form\Form */
                if ($err = $this->getErrors($child)) {
                    $errors[$key] = $err;
                }
            }
        }

        return $errors;
    }
}

/src/Acme/MyBundle/Controller/DefaultController.php

$form = $this->createFormBuilder($entity)->getForm();
$form_errors = $this->get('form_errors')->getArray($form);
return new JsonResponse($form_errors);

Symfony 2.5ではすべてのフィールドのエラーを非常に簡単に取得できます:

    $errors = array();
    foreach ($form as $fieldName => $formField) {
        foreach ($formField->getErrors(true) as $error) {
            $errors[$fieldName] = $error->getMessage();
        }
    }
3
Lebnik

バリデータサービスを使用して、制約違反を取得することもできます。

$errors = $this->get('validator')->validate($user);
3
antoinet

カスタムバリデーターを使用している場合、Symfonyは$form->getErrors()でそれらのバリデーターによって生成されたエラーを返しません。 $form->getErrorsAsString()は必要なすべてのエラーを返しますが、残念ながらその出力は配列ではなく文字列としてフォーマットされます。

すべてのエラーを取得するために使用する方法は(発生元に関係なく)、使用しているSymfonyのバージョンによって異なります。

推奨されるソリューションのほとんどは、すべての子フォームをスキャンし、関連するエラーを1つの配列に抽出する再帰関数を作成することを伴います。 symfony 2.3には$form->hasChildren()関数はありませんが、$form->all()はあります。

Symfony 2.3のヘルパークラスを次に示します。これを使用して、任意のフォームからすべてのエラーを抽出できます。 (Symfonyのgithubアカウントの関連バグチケットに対するyaproのコメントからのコードに基づいています。)

namespace MyApp\FormBundle\Helpers;

use Symfony\Component\Form\Form;

class FormErrorHelper
{
    /**
     * Work-around for bug where Symfony (2.3) does not return errors from custom validaters,
     * when you call $form->getErrors().
     * Based on code submitted in a comment here by yapro:
     * https://github.com/symfony/symfony/issues/7205
     *
     * @param Form $form
     * @return array Associative array of all errors
     */
    public function getFormErrors($form)
    {
        $errors = array();

        if ($form instanceof Form) {
            foreach ($form->getErrors() as $error) {
                $errors[] = $error->getMessage();
            }

            foreach ($form->all() as $key => $child) {
                /** @var $child Form */
                if ($err = $this->getFormErrors($child)) {
                    $errors[$key] = $err;
                }
            }
        }

        return $errors;
    }
}

呼び出しコード:

namespace MyApp\ABCBundle\Controller;

use MyApp\FormBundle\Helpers;

class MyController extends Controller
{
    public function XYZAction()
    {
        // Create form.

        if (!$form->isValid()) {
            $formErrorHelper = new FormErrorHelper();
            $formErrors = $formErrorHelper->getFormErrors($form);

            // Set error array into twig template here.
        }
    }

}
2
Jay Sheth

@Jay Sethの回答に基づいて、特にAjax Forms用のFormErrorsクラスのバージョンを作成しました。

// src/AppBundle/Form/FormErrors.php
namespace AppBundle\Form;

class FormErrors
{

    /**
     * @param \Symfony\Component\Form\Form $form
     *
     * @return array $errors
     */
    public function getArray(\Symfony\Component\Form\Form $form)
    {
        return $this->getErrors($form, $form->getName());
    }

    /**
     * @param \Symfony\Component\Form\Form $baseForm
     * @param \Symfony\Component\Form\Form $baseFormName
     *
     * @return array $errors
     */
    private function getErrors($baseForm, $baseFormName) {
        $errors = array();
        if ($baseForm instanceof \Symfony\Component\Form\Form) {
            foreach($baseForm->getErrors() as $error) {
                $errors[] = array(
                    "mess"      => $error->getMessage(),
                    "key"       => $baseFormName
                );
            }

            foreach ($baseForm->all() as $key => $child) {
                if(($child instanceof \Symfony\Component\Form\Form)) {
                    $cErrors = $this->getErrors($child, $baseFormName . "_" . $child->getName());
                    $errors = array_merge($errors, $cErrors);
                }
            }
        }
        return $errors;
    }
}

使用法(アクションなど):

$errors = $this->get('form_errors')->getArray($form);

Symfonyバージョン:2.8.4

JSON応答の例:

{
    "success": false,
    "errors": [{
        "mess": "error_message",
        "key": "RegistrationForm_user_firstname"
    }, {
        "mess": "error_message",
        "key": "RegistrationForm_user_lastname"
    }, {
        "mess": "error_message",
        "key": "RegistrationForm_user_email"
    }, {
        "mess": "error_message",
        "key": "RegistrationForm_user_zipCode"
    }, {
        "mess": "error_message",
        "key": "RegistrationForm_user_password_password"
    }, {
        "mess": "error_message",
        "key": "RegistrationForm_terms"
    }, {
        "mess": "error_message2",
        "key": "RegistrationForm_terms"
    }, {
        "mess": "error_message",
        "key": "RegistrationForm_marketing"
    }, {
        "mess": "error_message2",
        "key": "RegistrationForm_marketing"
    }]
}

エラーオブジェクトには、入力DOM要素のIDである「キー」フィールドが含まれているため、エラーメッセージを簡単に入力できます。

親の中に子フォームがある場合は、親フォームのsetDefaultsの中にcascade_validationオプションを追加することを忘れないでください。

2
RobbeR

SYMFONY 3.X

ここに記載されている他のSF 3.Xメソッドは、空のデータをフォームに送信できるため機能しませんでした(ただし、NotNull/NotBlanck制約があります)。この場合、エラー文字列は次のようになります。

string(282) "ERROR: This value should not be blank.
ERROR: This value should not be blank.
ERROR: This value should not be blank.
ERROR: This value should not be blank.
ERROR: This value should not be blank.
ERROR: This value should not be null.
name:
    ERROR: This value should not be blank.
"

これはあまり役に立ちません。だから私はこれを作りました:

public function buildErrorArray(FormInterface $form)
{
    $errors = [];

    foreach ($form->all() as $child) {
        $errors = array_merge(
            $errors,
            $this->buildErrorArray($child)
        );
    }

    foreach ($form->getErrors() as $error) {
        $errors[$error->getCause()->getPropertyPath()] = $error->getMessage();
    }

    return $errors;
}

それはそれを返します:

array(7) {
  ["data.name"]=>
  string(31) "This value should not be blank."
  ["data.street"]=>
  string(31) "This value should not be blank."
  ["data.zipCode"]=>
  string(31) "This value should not be blank."
  ["data.city"]=>
  string(31) "This value should not be blank."
  ["data.state"]=>
  string(31) "This value should not be blank."
  ["data.countryCode"]=>
  string(31) "This value should not be blank."
  ["data.organization"]=>
  string(30) "This value should not be null."
}
2
sbouba

私はこの解決策を思いつきました。最新のSymfony 2.4で動作します。

私はいくつかの説明をしようとします。

別のバリデーターを使用する

別の検証を使用してエンティティを検証し、他のライターが提案するような制約違反メッセージを返すことは悪い考えだと思います。

  1. すべてのエンティティを手動で検証したり、検証グループを指定したりする必要があります。複雑な階層形式では、まったく実用的ではなく、すぐに手に負えなくなります。

  2. この方法では、フォームを2回検証します。1回はフォームを使用し、もう1回は個別のバリデータを使用します。これはパフォーマンスの観点からは悪い考えです。

エラーメッセージを収集するために、子を使用してフォームタイプを再帰的に繰り返すことをお勧めします。

排他的IFステートメントでいくつかの推奨メソッドを使用する

別の著者によって提案された回答の中には、if ($form->count() > 0)またはif ($form->hasChildren())のような相互排他的なIFステートメントが含まれています。

私の知る限り、すべてのフォームにはエラーがあり、子もあります。私はSymfony Formsコンポーネントの専門家ではありませんが、実際にはCSRF保護エラーまたはextra fieldsエラー。この分離を削除することをお勧めします。

非正規化された結果構造の使用

一部の著者は、すべてのエラーを単純な配列内に配置することを提案しています。したがって、フォーム自体とその子のすべてのエラーメッセージは、異なるインデックス付け戦略を使用して同じ配列に追加されます。タイプ固有のエラーは数値ベース、子エラーは名前ベースです。フォームの正規化されたデータ構造を使用することをお勧めします。

errors:
    - "Self error"
    - "Another self error"

children
    - "some_child":
        errors:
            - "Children error"
            - "Another children error"

        children
            - "deeper_child":
                errors:
                    - "Children error"
                    - "Another children error"

    - "another_child":
        errors:
            - "Children error"
            - "Another children error"

このようにして、結果を後で簡単に繰り返すことができます。

私の解決策

したがって、この問題に対する私の解決策は次のとおりです。

use Symfony\Component\Form\Form;

/**
 * @param Form $form
 * @return array
 */
protected function getFormErrors(Form $form)
{
    $result = [];

    // No need for further processing if form is valid.
    if ($form->isValid()) {
        return $result;
    }

    // Looking for own errors.
    $errors = $form->getErrors();
    if (count($errors)) {
        $result['errors'] = [];
        foreach ($errors as $error) {
            $result['errors'][] = $error->getMessage();
        }
    }

    // Looking for invalid children and collecting errors recursively.
    if ($form->count()) {
        $childErrors = [];
        foreach ($form->all() as $child) {
            if (!$child->isValid()) {
                $childErrors[$child->getName()] = $this->getFormErrors($child);
            }
        }
        if (count($childErrors)) {
            $result['children'] = $childErrors;
        }
    }

    return $result;
}

私はそれが誰かを助けることを願っています。

1
Slava Fomin II

$ form-> getErrors()は私のために機能します。

1
ahyong

Twigエラー表示で使用するSymfony 2.1以降では、単にエラーを取得するのではなく、FormErrorを追加するように関数を変更しました。これにより、エラーをより細かく制御でき、個々の入力でerror_bubblingを使用する必要がなくなります。以下の方法で設定しない場合、{{form_errors(form)}}は空白のままになります。

/**
 * @param \Symfony\Component\Form\Form $form
 *
 * @return void
 */
private function setErrorMessages(\Symfony\Component\Form\Form $form) {      

    if ($form->count() > 0) {
        foreach ($form->all() as $child) {
            if (!$child->isValid()) {
                if( isset($this->getErrorMessages($child)[0]) ) {
                    $error = new FormError( $this->getErrorMessages($child)[0] );
                    $form->addError($error);
                }
            }
        }
    }

}

Symfony 3.2以上ではこれを使用し、

public function buildErrorArray(FormInterface $form)
{
    $errors = array();

    foreach ($form->getErrors() as $key => $error) {
        if ($form->isRoot()) {
            $errors['#'][] = $error->getMessage();
        } else {
            $errors[] = $error->getMessage();
        }
    }

    foreach ($form->all() as $child) {
        if (!$child->isValid()) {
            $errors[$child->getName()] = (string) $child->getErrors(true, false);
        }
    }
    return $errors;
}

各エラーの説明テキストの迷惑な「Error:」テキストを削除する場合は、str_replaceを使用します。

$errors[$child->getName()] = str_replace('ERROR:', '', (string) $child->getErrors(true, false));
1
Anjana Silva

SYMFONY 3.1

エラーの表示を処理する静的メソッドを実装しただけです

static function serializeFormErrors(Form\Form $form)
{
    $errors = array();
    /**
     * @var  $key
     * @var Form\Form $child
     */
    foreach ($form->all() as $key => $child) {
        if (!$child->isValid()) {
            foreach ($child->getErrors() as $error) {
                $errors[$key] = $error->getMessage();
            }
        }
    }

    return $errors;
}

助けたい

1
Shigiang Liu

Symfony 2.1の場合:

これは、他の多くのソリューションをまとめる最終的なソリューションです。

protected function getAllFormErrorMessages($form)
{
    $retval = array();
    foreach ($form->getErrors() as $key => $error) {
        if($error->getMessagePluralization() !== null) {
            $retval['message'] = $this->get('translator')->transChoice(
                $error->getMessage(), 
                $error->getMessagePluralization(), 
                $error->getMessageParameters(), 
                'validators'
            );
        } else {
            $retval['message'] = $this->get('translator')->trans($error->getMessage(), array(), 'validators');
        }
    }
    foreach ($form->all() as $name => $child) {
        $errors = $this->getAllFormErrorMessages($child);
        if (!empty($errors)) {
           $retval[$name] = $errors; 
        }
    }
    return $retval;
}
0
Fernando P. G.