Symfony2: Wie bekomme ich Formularvalidierungserrors nach dem Binden der Anfrage an das Formular?

Hier ist mein saveAction Code (wo das Formular die Daten 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(); } 

Meine Frage ist: Wie bekomme ich die Fehler, wenn $form->isValid() false zurückgibt?

    Sie haben zwei Möglichkeiten, dies zu tun:

    • Benutzer bei Fehler nicht redirect und {{ form_errors(form) }} in der Vorlagendatei anzeigen
    • Zugriffserrorsarray als $form->getErrors()

    Symfony 2.3 / 2.4:

    Diese function bekommt alle Fehler. Die auf dem Formular wie “Das CSRF-Token ist ungültig. Bitte versuchen Sie das Formular erneut zu senden.” sowie zusätzliche Fehler auf den Form-Children, die keinen Fehler haben.

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

    Um alle Fehler als String zu erhalten:

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

    Symfony 2.5 / 3.0:

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

    Dokumente:
    https://github.com/symfony/symfony/blob/master/UPGRADE-2.5.md#form https://github.com/symfony/symfony/blob/master/UPGRADE-3.0.md#form (unten) : The method Form::getErrorsAsString() was removed )

    Unten ist die Lösung, die für mich funktioniert hat. Diese function befindet sich in einem Controller und gibt ein strukturiertes Array aller Fehlermeldungen und des Felds zurück, das sie verursacht hat.

    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 und neuer:

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

    Verwenden Sie den Validator, um die Fehler für eine bestimmte Entität abzurufen

     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-Referenz:

    • ConstraintViolationList
    • Constraint-Verletzung

    Um korrekte (übersetzbare) Nachrichten zu erhalten, die momentan SF 2.6.3 verwenden, ist hier meine letzte function (da keine der oben genannten mehr zu funktionieren scheint):

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

    Die Methode Form :: getErrors () gibt jetzt eine Instanz von FormErrorIterator zurück , sofern Sie das zweite Argument ($ flatten) nicht auf true setzen . (Es wird dann eine FormError- Instanz zurückgegeben, und Sie müssen die Methode getMessage () direkt aufrufen, ohne die Methode current ():

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

    )

    Die wichtigste Sache ist eigentlich, das erste Argument auf wahr zu setzen, um die Fehler zu bekommen. Wenn das zweite Argument ($ flatten) auf seinem Standardwert ( true ) belassen wird, werden die FormError- Instanzen zurückgegeben, während bei der Einstellung false die FormErrorIterator- Instanzen zurückgegeben werden.

    Die function für symfony 2.1 und neuer, ohne veraltete function:

     /** * @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; } 

    Für meine Flash-Nachrichten war ich glücklich mit $form->getErrorsAsString()

    Edit (von Benji_X80): Für SF3 benutze $form->getErrors(true, false);

    Übersetzte Form Fehlermeldungen (Symfony2.1)

    Ich habe mich viel Mühe gegeben, diese Informationen zu finden, daher denke ich, dass es sich lohnt, einen Hinweis zur Übersetzung von Formularerrorsn hinzuzufügen.

    @Icode4food Antwort gibt alle Fehler eines Formulars zurück. Das Array, das zurückgegeben wird, berücksichtigt jedoch weder die Nachrichtenpluralisierung noch die Übersetzung .

    Sie können die foreach-Schleife der @Icode4food Antwort @Icode4food , um eine Combo zu haben:

    • Erhalten Sie alle Fehler eines bestimmten Formulars
    • Gib einen übersetzten Fehler zurück
    • Berücksichtigen Sie bei Bedarf die Pluralisierung

    Hier ist es:

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

    Diese Antwort wurde aus 3 verschiedenen Posts zusammengestellt:

    • Symfony2 – Übersetzte validationserrors im Controller bekommen
    • Verwendung der Symfony2-validationskomponente ohne Formulare (Entitäten und Datenfelder)
    • Symfony2 – Abrufen aller Fehler von einem Formular in einem Controller

    Sie können den Validiererdienst auch verwenden, um Einschränkungsverletzungen zu erhalten:

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

    Übersetzte Form Fehlermeldungen (Symfony2.3)

    Meine Version der Lösung des Problems:

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

    In Symfony 2.5 können Sie alle Felder Fehler sehr einfach bekommen:

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

    Wenn Sie benutzerdefinierte Validatoren verwenden, gibt Symfony keine Fehler zurück, die von diesen Validatoren in $form->getErrors() generiert wurden. $form->getErrorsAsString() gibt alle Fehler zurück, die Sie benötigen, aber die Ausgabe ist leider als String und nicht als Array formatiert.

    Die Methode, mit der Sie alle Fehler erhalten (unabhängig davon, woher sie stammen), hängt davon ab, welche Version von Symfony Sie verwenden.

    Die meisten vorgeschlagenen Lösungen beinhalten das Erstellen einer rekursiven function, die alle untergeordneten Formulare scannt und die relevanten Fehler in ein Array extrahiert. Symfony 2.3 hat nicht die function $form->hasChildren() , aber es hat $form->all() .

    Hier ist eine Hilfsklasse für Symfony 2.3, mit der Sie alle Fehler aus jedem Formular extrahieren können. (Es basiert auf Code aus einem Kommentar von yapro zu einem verwandten Fehlerticket in Symfonys GitHub-Account.)

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

    Vorwahlcode:

     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. } } } 

    Basierend auf @Jay Seths Antwort habe ich eine Version der FormErrors-class speziell für Ajax-Formulare erstellt:

     // 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; } } 

    Verwendung (zB in Ihrer Aktion):

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

    Symfony-Version: 2.8.4

    Beispiel für eine JSON-Antwort:

     { "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" }] } 

    Das error-Objekt enthält das “Schlüssel” -Feld, das die ID des DOM-Eingabeelements ist, sodass Sie Fehlermeldungen problemlos auffüllen können.

    Wenn Sie cascade_validation Formulare haben, vergessen Sie nicht, die Option cascade_validation in cascade_validation des übergeordneten Formulars setDefaults .

    SYMFONY 3.X

    Andere SF 3.X-Methoden, die hier angegeben sind, funktionierten nicht für mich, weil ich leere Daten an das Formular senden konnte (aber ich habe NotNull / NotBlanck-Einschränkungen). In diesem Fall würde die Fehlerzeichenfolge wie folgt aussehen:

     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. " 

    Was nicht sehr nützlich ist. Also habe ich Folgendes gemacht:

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

    Was würde das zurückgeben:

     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." } 

    Ab Symfony 2.1 zur Verwendung mit der Twig-Fehleranzeige habe ich die function geändert, um einen FormError hinzuzufügen, anstatt sie einfach abzurufen. Auf diese Weise haben Sie mehr Kontrolle über Fehler und müssen nicht für jede einzelne Eingabe error_bubbling verwenden. Wenn Sie es nicht wie unten beschrieben festlegen, bleibt {{form_errors (form)}} leer:

     /** * @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); } } } } } 

    $ form-> getErrors () funktioniert für mich.

    Ich habe diese Lösung gefunden. Es arbeitet solide mit der neuesten Symfony 2.4 .

    Ich werde versuchen, einige Erklärungen zu geben.

    Mit separatem Validator

    Ich denke, es ist eine schlechte Idee, separate validation zu verwenden, um Entitäten zu validieren und Constraint-Verletzungsnachrichten zurückzugeben, wie von anderen Autoren vorgeschlagen.

    1. Sie müssen alle Entitäten manuell validieren, validationsgruppen usw. angeben. Mit komplexen hierarchischen Formen ist es überhaupt nicht praktikabel und wird schnell außer Kontrolle geraten.

    2. Auf diese Weise werden Sie das Formular zweimal bestätigen: einmal mit Formular und einmal mit separatem Validator. Dies ist eine schlechte Idee aus der performancesperspektive.

    Ich schlage vor, Formulartyp mit seinen Kindern rekursiv zu wiederholen, um Fehlermeldungen zu sammeln.

    Einige vorgeschlagene Methoden mit exklusiver IF-statement verwenden

    Einige Antworten, die von anderen Autoren vorgeschlagen werden, enthalten sich gegenseitig ausschließende IF-statementen wie if ($form->count() > 0) : if ($form->count() > 0) oder if ($form->hasChildren()) .

    Soweit ich sehen kann, kann jede Form Fehler haben, ebenso wie Kinder. Ich bin kein Experte mit Symfony Forms- Komponente, aber in der Praxis werden Sie einige Fehler des Formulars selbst, wie CSRF-Schutzerrors oder zusätzliche Felder Fehler nicht erhalten. Ich schlage vor, diese Trennung zu entfernen.

    Denormierte Ergebnisstruktur verwenden

    Einige Autoren schlagen vor, alle Fehler innerhalb eines einfachen Arrays zu platzieren. Daher werden alle Fehlermeldungen des Formulars selbst und seiner untergeordneten Elemente demselben Array mit unterschiedlichen Indexierungsstrategien hinzugefügt: nummersbasiert für eigene Fehler des Typs und namenbasiert für Kindererrors. Ich schlage vor, normalisierte Datenstruktur des Formulars zu verwenden:

     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" 

    Auf diese Weise kann das Ergebnis später einfach wiederholt werden.

    Meine Lösung

    Also hier ist meine Lösung für dieses Problem:

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

    Ich hoffe, es wird jemandem helfen.

    Für Symfony 3.2 und höher verwenden Sie

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

    Verwenden Sie str_replace, wenn Sie den störenden ‘ Error: ‘ Text in jedem Fehlerbeschreibungstext loswerden wollen.

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

    Für Symfony 2.1:

    Dies ist meine letzte Lösung, die viele andere Lösungen zusammenführt:

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

    SYMFONIE 3.1

    Ich habe einfach eine statische Methode implementiert, um die Anzeige von Fehlern zu behandeln

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

    Ich hoffe auf Hilfe