Search code examples
jsonsymfonytwigformbuilderrequiredfieldvalidator

How can I display required fields error (validation) message with symfony formbuilder?


PagesController.php

$id = $request->request->get('id');
$target = $request->request->get('target');
$EntityName = 'App\\Entity\\' . ucwords($slug);

$em = $this->getDoctrine()->getManager();
$cmf = $em->getMetadataFactory();
$classes = $cmf->getMetadataFor($EntityName);


if($request->request->get('target')){

  $item = new $EntityName();
  $item= $this->getDoctrine()->getRepository($EntityName)->find($id);

  $formBuilder = $this->createFormBuilder($item);

  foreach ($classes->fieldMappings as $fieldMapping) {
      $formBuilder->add($fieldMapping['fieldName'], TextType::class, array('attr' => array('class' => 'form-control'), 'required'   => true,));
  }

  $formBuilder->add('cancel', ButtonType::class, array('label' => 'Cancel','attr' => array('class' => 'cancel form-btn btn btn-default pull-right close_sidebar close_h')))
  ->add('save', SubmitType::class, array('label' => 'Save','attr' => array('id' => 'submit-my-beautiful-form','class' => 'form-btn btn btn-info pull-right','style' => 'margin-right:5px')));
  $form = $formBuilder->getForm();

  $form->handleRequest($request);


  $response = new JsonResponse(
    array(
      'message' => 'Success',
      'output' => $this->renderView('form.html.twig',
      array(
        'target' => $target,
        'entity' => $item,
        'form' => $form->createView(),
      ))), 200);
      return $response;



} else {

   $em = $this->getDoctrine()->getManager();
    foreach ($classes->fieldMappings as $fieldMapping) {
      $func = 'set'.$fieldMapping['fieldName'];
      $args = $data['form['.$fieldMapping['fieldName'].']'];
      $entity->$func($args);
    }
   $em->persist($entity);
   $em->flush();
  $response = new JsonResponse(array('id' => $data['form[id]']), 200);
  return $response;
}

form.html.twig

<section class="content-header" style="margin-bottom:20px">
  <h1 style="float:left;margin-bottom:30px">Create Entry </h1>
</section>
<section class="content" style="clear:left">
  <div class="form-group">
    {{ form_start(form) }}
    {{ form_end(form) }}
  </section>

My form is working well, when I fill it out and press the "Save" Button it is stored in the database. When I leave all fields empty and press "Save" nothing is happening and I get a 500 Error

An exception occurred while executing 'INSERT INTO members (username, password, email, is_active) VALUES (?, ?, ?, ?)' with params ["", null, "", "1"]:

SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'password' cannot be null

This is actually fine, because the fields are required, but the errors are not displayed in my form, even if I added "required" = "true".


Solution

  • So when using the form builder, you have access to:

    $form->isSubmitted()
    

    and

    $form->isValid()
    

    As a result you can do something like:

    if ($form->isSubmitted() && $form->isValid()) {
        // save to database
    }
    

    We want to check the form is submitted before we check it's valid as if the form isn't submitted, it's unnecessary to check it's valid as it will be false.

    This is going to prevent your MySQL error because your form technically isn't valid and you're trying to flush invalid data. We obviously only want to save our data when it's valid.

    Of course, if the form fails you can return the view and in the template, you have access to

    {{ form_errors() }}
    

    This will probably cover what you need but you could also pass to your template something like

    'formHasErrors' => $form->isSubmitted() && !$form->isValid(),
    

    and then in your template

    {% if formHasErrors %}
    

    Something else you may want to do, this allows you to have more control over your fields, is separate out the field out like below:

    {{ form_start(form) }}
        {{ form_label(form.name) }}
        {{ form_errors(form.name, {'attr': {'class': 'form-input'}}) }}
        {{ form_widget(form.name) }}
    {{ form_end(form) }}
    

    It is very important you catch errors and handle them correctly. Your implementation didn't verify the form is valid which is why you were getting 500 errors.