I\'m using Symfony2 forms to validate POST and PUT requests to an API. The form handles binding the request data to the underlying entity and then validating the entity. Eve
For Symfony 4.x+ (may working with lower versions).
// $form = $this->createForm(SomeType::class);
// $form->submit($data);
// if (!$form->isValid()) {
// var_dump($this->getErrorsFromForm($form));
// }
private function getErrorsFromForm(FormInterface $form, bool $child = false): array
{
$errors = [];
foreach ($form->getErrors() as $error) {
if ($child) {
$errors[] = $error->getMessage();
} else {
$errors[$error->getOrigin()->getName()][] = $error->getMessage();
}
}
foreach ($form->all() as $childForm) {
if ($childForm instanceof FormInterface) {
if ($childErrors = $this->getErrorsFromForm($childForm, true)) {
$errors[$childForm->getName()] = $childErrors;
}
}
}
return $errors;
}
You can take getErrorsAsString method as an example to get the functionality you want. Also you have to set invalid_message
option on the form field to change This value is invalid
message.
I created a bundle to help with this issue.
You can get it here:
https://github.com/Ex3v/FormErrorsBundle
Contributions are welcome. Cheers.
I needed a solution to get an associative array of all errors of all nested forms with the key formated so it represents the document id of the corresponding form field element:
namespace Services;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormErrorIterator;
/**
* Class for converting forms.
*/
class FormConverter
{
/**
* Gets all errors of a form as an associative array with keys representing the dom id of the form element.
*
* @param Form $form
* @param bool $deep Whether to include errors of child forms as well
* @return array
*/
public function errorsToArray(Form $form, $deep = false) {
return $this->getErrors($form, $deep);
}
/**
* @param Form $form
* @param bool $deep
* @param Form|null $parentForm
* @return array
*/
private function getErrors(Form $form, $deep = false, Form $parentForm = null) {
$errors = [];
if ($deep) {
foreach ($form as $child) {
if ($child->isSubmitted() && $child->isValid()) {
continue;
}
$iterator = $child->getErrors(true, false);
if (0 === count($iterator)) {
continue;
} elseif ($iterator->hasChildren()) {
$childErrors = $this->getErrors($iterator->getChildren()->getForm(), true, $child);
foreach ($childErrors as $key => $childError) {
$errors[$form->getName() . '_' . $child->getName() . '_' .$key] = $childError;
}
} else {
foreach ($iterator as $error) {
$errors[$form->getName() . '_' . $child->getName()][] = $error->getMessage();
}
}
}
} else {
$errorMessages = $this->getErrorMessages($form->getErrors(false));
if (count($errorMessages) > 0) {
$formName = $parentForm instanceof Form ? $parentForm->getName() . '_' . $form->getName() : $form->getName();
$errors[$formName] = $errorMessages;
}
}
return $errors;
}
/**
* @param FormErrorIterator $formErrors
* @return array
*/
private function getErrorMessages(FormErrorIterator $formErrors) {
$errorMessages = [];
foreach ($formErrors as $formError) {
$errorMessages[] = $formError->getMessage();
}
return $errorMessages;
}
}
Tried everything, nothing worked as I wanted.
Here is my attempt with Symfony 4 (also why the hell isn't this available by default in the framework is beyond me, it's like they dont want us to use forms with ajax :thinking:)
It returns a messages array, with the keys being the dom id as it would be generated by Symfony (it works with arrays as well)
//file FormErrorsSerializer.php
<?php
namespace App\Helper;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormErrorIterator;
class FormErrorsSerializer
{
public function getFormErrors(Form $form): array
{
return $this->recursiveFormErrors($form->getErrors(true, false), [$form->getName()]);
}
private function recursiveFormErrors(FormErrorIterator $formErrors, array $prefixes): array
{
$errors = [];
foreach ($formErrors as $formError) {
if ($formError instanceof FormErrorIterator) {
$errors = array_merge($errors, $this->recursiveFormErrors($formError, array_merge($prefixes, [$formError->getForm()->getName()])));
} elseif ($formError instanceof FormError) {
$errors[implode('_', $prefixes)][] = $formError->getMessage();
}
}
return $errors;
}
}
Use this function to get all error messages after binding form.
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;
}