Skip to content
Advertisement

How to display validation errors in JSON of associated fields in Cakephp4?

I’m looking for a way to display validation errors in jQuery after sending a form in ajax that returns validation errors in JSON.

My code works for simple form, but the difficulties appear for more complex forms, especially for forms containing inputs for associated data.

For example :

Here is a form containing input files and associated data for writing captions :

// Articles/edit.php
$this->Form->create($article);
echo $this->Form->control('title');
echo $this->Form->control('photos.0.legend');
echo $this->Form->control('photos.1.legend');
$this->Form->end();

I post the form in ajax to my ArticlesController‘s edit method that looks like that :

// ArticlesController.php
public function edit($id)
{
    $article = $this->Articles->findById($id)->firstOrFail();

    if ($this->request->is(['post', 'put'])) {
        $article = $this->Articles->patchEntity($article, $this->request->getData());

        if ($this->Articles->save($article)) {
            $redirection = ['action' => 'index'];

            if ($this->request->is('ajax')) {
                die(json_encode(['error' => false, 'redirection' => Router::url($redirection)]));
            }
            return $this->redirect($redirection);
        }
        else {
            if ($this->request->is('ajax')) {
                die(json_encode(['error' => true, 'validationErrors' => $article->getErrors()]));
            }
        }
    }
    $this->set(compact('article'));
}

Here is the return of validation errors in JSON :

{
  "error": true,
  "validationErrors": {
    "title": {
      "_empty": "The title please."
    },
    "photos": { // validation errors are nested
      "1": {
        "legend": {
          "_empty": "The legend please."
        }
      }
    }
  }
}

Here is how I’m tring to display validation errors :

// Articles/edit.php
$('form')
    .fileupload({   // It sends the form in ajax
        dataType: 'json',
        // [...]
        done: function (e, data) {
                var responseJSON = data.jqXHR.responseJSON;
                if (!responseJSON.error) { // There's no error => redirect
                    window.location = responseJSON.redirection;
                }
                else { // The form contains validation errors
                    $.each(responseJSON.validationErrors, function (field, errors) {
                        displayFieldErrors(field, errors);
                    });
                }
            },
         // [...]
     });

// Display validation errors in a field 
function displayFieldErrors(field, errors)
{
    var fieldId = field.replace(/_/, '-');
    var input = $("#" + fieldId);
    var errorMessage = '';
    $.each(errors, function (key, message) {
        errorMessage = errorMessage + message + '<br>';
    });
    input.parents('.input').addClass('error');
    input.after('<div class="error-message">' + errorMessage + '</div>');
}

The problem is that validation errors for associated data fields are nested (e.g photos), so how to get the input’s id involved ?

Could anyone help me to find a correct implementation for my jQuery function displayFieldErrors() ?

Advertisement

Answer

I’ve solved the problem by returning formated validation errors from a JSON View (server side) :

// in Articles/json/edit.php
$errors = [];
// Recursive function to build `$errors[]` associating each field in error to one or many error message(s)
$fieldsInError = function ($err, $buildingField = '') use (&$errors, &$fieldsInError) {
    foreach ($err as $key => $value) {
        if (is_array($value)) {
            $fieldsInError($value, empty($buildingField) ? $key : $buildingField . '.' . $key); // recursive
        }
        else { // string
            if (isset($errors[$buildingField])) {
                if (is_array($errors[$buildingField])) {
                    $errors[$buildingField][] = $value;
                }
                else {
                    $errors[$buildingField] = [$errors[$buildingField], $value];
                }
            }
            else {
                $errors += [$buildingField => $value];
            }
        }
    }
};
$fieldsInError($article->getErrors());

echo json_encode(['error' => true, 'validationErrors' => $errors]));

JSON is now looking like that :

{
  "error": true,
  "validationErrors": {
    "title": "The title please.",
    "photos.1.legend": "The legend please."
  }
}

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement