I want to create a webservice to which I submit a form, and in case of errors, returns a JSON encoded list that tells me which field is wrong.
currently I only get a
I've finally found the solution to this problem here, it only needed a small fix to comply to latest symfony changes and it worked like a charm:
The fix consists in replacing line 33
if (count($child->getIterator()) > 0) {
with
if (count($child->getIterator()) > 0 && ($child instanceof \Symfony\Component\Form\Form)) {
because, with the introduction in symfony of Form\Button, a type mismatch will occur in serialize function which is expecting always an instance of Form\Form.
You can register it as a service:
services:
form_serializer:
class: Wooshii\SiteBundle\FormErrorsSerializer
and then use it as the author suggest:
$errors = $this->get('form_serializer')->serializeFormErrors($form, true, true);
This does the trick for me
$errors = [];
foreach ($form->getErrors(true, true) as $formError) {
$errors[] = $formError->getMessage();
}
This will do the trick. This static method runs recursively through the Symfony\Component\Form\FormErrorIterator
delivered by calling $form->getErrors(true, false)
.
<?php
namespace App\Utils;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormErrorIterator;
class FormUtils
{
public static function generateErrorsArrayFromForm(FormInterface $form)
{
$result = [];
foreach ($form->getErrors(true, false) as $formError) {
if ($formError instanceof FormError) {
$result[$formError->getOrigin()->getName()] = $formError->getMessage();
} elseif ($formError instanceof FormErrorIterator) {
$result[$formError->getForm()->getName()] = self::generateErrorsArrayFromForm($formError->getForm());
}
}
return $result;
}
}
Here is the result :
{
"houseworkSection": "All the data of the housework section must be set since the section has been requested.",
"foodSection": {
"requested": {
"requested": "This value is not valid."
}
}
}
PHP has associative arrays, meanwhile JS has 2 different data structures : object and arrays.
The JSON you want to obtain is not legal and should be :
{
"status":400,
"errorMsg":"Bad Request",
"errorReport": {
"taskfield" : "Task cannot be blank",
"taskdatefield" : "Task date needs to be within the month"
}
}
So you may want to do something like this to build your collection :
$errorCollection = array();
foreach($errors as $error){
$errorCollection[$error->getId()] = $error->getMessage();
}
(assuming the getId() method exist on $error objects)
I am using this, it works quiet well:
/**
* List all errors of a given bound form.
*
* @param Form $form
*
* @return array
*/
protected function getFormErrors(Form $form)
{
$errors = array();
// Global
foreach ($form->getErrors() as $error) {
$errors[$form->getName()][] = $error->getMessage();
}
// Fields
foreach ($form as $child /** @var Form $child */) {
if (!$child->isValid()) {
foreach ($child->getErrors() as $error) {
$errors[$child->getName()][] = $error->getMessage();
}
}
}
return $errors;
}
By reading other people's answers I ended up improving it to my needs. I use it in Symfony 3.4.
To be used in a controller like this:
$formErrors = FormUtils::getErrorMessages($form);
return new JsonResponse([
'formErrors' => $formErrors,
]);
With this code in a separate Utils class
<?php
namespace MyBundle\Utils;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormInterface;
class FormUtils
{
/**
* @param FormInterface $form
* @return array
*/
public static function getErrorMessages(FormInterface $form)
{
$formName = $form->getName();
$errors = [];
/** @var FormError $formError */
foreach ($form->getErrors(true, true) as $formError) {
$name = '';
$thisField = $formError->getOrigin()->getName();
$origin = $formError->getOrigin();
while ($origin = $origin->getParent()) {
if ($formName !== $origin->getName()) {
$name = $origin->getName() . '_' . $name;
}
}
$fieldName = $formName . '_' . $name . $thisField;
/**
* One field can have multiple errors
*/
if (!in_array($fieldName, $errors)) {
$errors[$fieldName] = [];
}
$errors[$fieldName][] = $formError->getMessage();
}
return $errors;
}
}