Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to return json encoded form errors in symfony

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 list of error messages but not an html id or a name of the fields with errors

here's my current code

public function saveAction(Request $request)
{
    $em = $this->getDoctrine()->getManager();

    $form = $this->createForm(new TaskType(), new Task());

    $form->handleRequest($request);

    $task = $form->getData();

    if ($form->isValid()) {

        $em->persist($task);
        $em->flush();

        $array = array( 'status' => 201, 'msg' => 'Task Created'); 

    } else {

        $errors = $form->getErrors(true, true);

        $errorCollection = array();
        foreach($errors as $error){
               $errorCollection[] = $error->getMessage();
        }

        $array = array( 'status' => 400, 'errorMsg' => 'Bad Request', 'errorReport' => $errorCollection); // data to return via JSON
    }

    $response = new Response( json_encode( $array ) );
    $response->headers->set( 'Content-Type', 'application/json' );

    return $response;
}

this will give me a response like

{
"status":400,
"errorMsg":"Bad Request",
"errorReport":{
        "Task cannot be blank",
        "Task date needs to be within the month"
    }
}

but what I really want is something like

{
"status":400,
"errorMsg":"Bad Request",
"errorReport":{
        "taskfield" : "Task cannot be blank",
        "taskdatefield" : "Task date needs to be within the month"
    }
}

How can I achieve that?

like image 983
SimonQuest Avatar asked Jul 03 '14 14:07

SimonQuest


4 Answers

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;
}
like image 175
COil Avatar answered Oct 11 '22 13:10

COil


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);
like image 33
SimonQuest Avatar answered Oct 11 '22 14:10

SimonQuest


This does the trick for me

 $errors = [];
 foreach ($form->getErrors(true, true) as $formError) {
    $errors[] = $formError->getMessage();
 }
like image 8
iamtankist Avatar answered Oct 11 '22 15:10

iamtankist


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)

like image 1
Delapouite Avatar answered Oct 11 '22 13:10

Delapouite