title: Symfony 3 - Edit form - populate fields with db data (array)
body: <p>I needed a form with 3 dynamic select boxes (choicetypes), which, after form submission, are saved as a serialized array in the database.</p>
  <p>I have managed to make it work in the end but now I am struggling to populate the dropdowns with the database values when editing a project.</p>
  <p><strong>Projects Entity</strong></p>
  <pre><code>&lt;?php
  
  namespace AppBundle\Entity;
  use Doctrine\ORM\Mapping as ORM;
  
  /**
   * @ORM\Entity
   * @ORM\Table(name=&quot;projects&quot;)
   */
  class Projects
  {
      /**
       * @ORM\Column(type=&quot;string&quot;, length=255)
       */
      protected $projectName;
      /**
       * @ORM\Column(type=&quot;array&quot;)
       */
      protected $frequency;
      /**
       * No database fields for these.
       * They are used just to populate the form fileds
       * and the serialized values are stored in the frequency field
       */
       protected $update;
       protected $every;
       protected $on;
  
      /*
       * Project Name
       */
      public function getProjectName()
      {
          return $this-&gt;projectName;
      }
      public function setProjectName($projectName)
      {
          $this-&gt;projectName = $projectName;
      }
      /*
       * Frequency
       */
      public function getFrequency()
      {
          return $this-&gt;frequency;
      }
      public function setFrequency($frequency)
      {
          $this-&gt;frequency = $frequency;
      }
      /*
       * Update
       */  
      public function getUpdate()
      {
          return $this-&gt;update;
      }
      public function setUpdate($update)
      {
          $this-&gt;update = $update;
      }
      /*
       * Every
       */  
      public function getEvery()
      {
          return $this-&gt;every;
      }
      public function setEvery($every)
      {
          $this-&gt;every = $every;
      }
      /*
       * On
       */  
      public function getOn()
      {
          return $this-&gt;on;
      }
      public function setOn($on)
      {
          $this-&gt;on = $on;
      }
  }
  </code></pre>
  <p><strong>Projects Controller</strong></p>
  <pre><code>&lt;?php
  
  namespace AppBundle\Controller;
  
  use AppBundle\Entity\Projects;
  use Symfony\Bundle\FrameworkBundle\Controller\Controller;
  use Symfony\Component\HttpFoundation\Request;
  use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
  use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  
  /**
   * Projects controller.
   */
  class ProjectsController extends Controller
  {
      /**
       * @Route(&quot;/projects/new&quot;, name=&quot;projects_new&quot;)
       */
      public function newAction(Request $request)
      {
          $project = new Projects();
          $form = $this-&gt;createForm('AppBundle\Form\ProjectsType', $project);
          $form-&gt;handleRequest($request);
  
          if ($form-&gt;isSubmitted() &amp;&amp; $form-&gt;isValid()) {
              $em = $this-&gt;getDoctrine()-&gt;getManager();
              // This is where the data get's saved in the frequency field
              $frequency = array(
                  &quot;update&quot; =&gt; $project-&gt;getUpdate(),
                  &quot;every&quot; =&gt; $project-&gt;getEvery(),
                  &quot;on&quot; =&gt; $project-&gt;getOn()
              );
              $project-&gt;setFrequency($frequency);
  
              $em-&gt;persist($project);
              $em-&gt;flush($project);
  
              return $this-&gt;redirectToRoute('projects_show', array('id' =&gt; $project-&gt;getId()));
          }
  
          return $this-&gt;render('projects/new.html.twig', array(
              'project' =&gt; $project,
              'form' =&gt; $form-&gt;createView(),
          ));
      }
  
      /**
       * @Route(&quot;/projects/edit/{id}&quot;, name=&quot;projects_edit&quot;, requirements={&quot;id&quot;: &quot;\d+&quot;})
       * @ParamConverter(&quot;id&quot;, class=&quot;AppBundle:Projects&quot;)
       */
      public function editAction(Request $request, Projects $project)
      {
          $editForm = $this-&gt;createForm('AppBundle\Form\ProjectsType', $project);
          $editForm-&gt;handleRequest($request);
  
          if ($editForm-&gt;isSubmitted() &amp;&amp; $editForm-&gt;isValid()) {
              // This is where the data get's saved in the frequency field
              $frequency = array(
                  &quot;update&quot; =&gt; $project-&gt;getUpdate(),
                  &quot;every&quot; =&gt; $project-&gt;getEvery(),
                  &quot;on&quot; =&gt; $project-&gt;getOn()
              );
              $project-&gt;setFrequency($frequency);
  
              $this-&gt;getDoctrine()-&gt;getManager()-&gt;flush();
  
              return $this-&gt;redirectToRoute('projects_edit', array('id' =&gt; $project-&gt;getId()));
          }
  
          return $this-&gt;render('projects/edit.html.twig', array(
              'project' =&gt; $project,
              'edit_form' =&gt; $editForm-&gt;createView(),
              'delete_form' =&gt; $deleteForm-&gt;createView(),
          ));
      }
  }
  </code></pre>
  <p><strong>ProjectsType Form</strong></p>
  <pre><code>&lt;?php
  
  namespace AppBundle\Form;
  
  use Symfony\Component\Form\AbstractType;
  use Symfony\Component\Form\FormBuilderInterface;
  use Symfony\Component\OptionsResolver\OptionsResolver;
  use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
  use Symfony\Component\Form\FormEvent;
  use Symfony\Component\Form\FormEvents;
  use Symfony\Component\Form\FormInterface;
  use Symfony\Component\Form\FormView;
  use AppBundle\Entity\Projects;
  
  class ProjectsType extends AbstractType
  {
      private function setUpdateChoice(Projects $project)
      {
          return $project-&gt;getFrequency()['update'];
      }
  
      /**
       * {@inheritdoc}
       */
      public function buildForm(FormBuilderInterface $builder, array $options)
      {
          $builder-&gt;add('projectName', null, array());
  
          $builder-&gt;add('update', ChoiceType::class, array(
                  'label'         =&gt; 'Update',
                  'attr'          =&gt; array(
                      'class' =&gt; 'update_selector',
                  ),
                  'choices' =&gt; array(
                      'Daily' =&gt; 'Daily',
                      'Weekly' =&gt; 'Weekly',
                      'Monthly' =&gt; 'Monthly'
                  ),
                  'data' =&gt; $this-&gt;setUpdateChoice($builder-&gt;getData())
              )
          );
  
          $addFrequencyEveryField = function (FormInterface $form, $update_val) {
              $choices = array();
              switch ($update_val) {
                  case 'Daily':
                      $choices = array('1' =&gt; '1', '2' =&gt; '2');
                      break;
  
                  case 'Weekly':
                      $choices = array('Week' =&gt; 'Week', '2 Weeks' =&gt; '2 Weeks');
                      break;
  
                  case 'Monthly':
                      $choices = array('Month' =&gt; 'Month', '2 Months' =&gt; '2 Months');
                      break;
  
                  default:
                      $choices = array();
                      break;
              }
          };
    
          $builder-&gt;addEventListener(
              FormEvents::PRE_SET_DATA,
              function (FormEvent $event) use ($addFrequencyEveryField) {
                  $update = $event-&gt;getData()-&gt;getUpdate();
                  $update_val = $update ? $update-&gt;getUpdate() : null;
                  $addFrequencyEveryField($event-&gt;getForm(), $update_val);
              }
          );
          $builder-&gt;addEventListener(
              FormEvents::PRE_SUBMIT,
              function (FormEvent $event) use ($addFrequencyEveryField) {
                  $data = $event-&gt;getData();
                  $update_val = array_key_exists('update', $data) ? $data['update'] : null;
                  $addFrequencyEveryField($event-&gt;getForm(), $update_val);
              }
          );
  
          $addFrequencyOnField = function (FormInterface $form, $every_val) {
              $choicesOn = array();
              switch ($every_val) {
                  case 'Week':
                      $choicesOn = array(
                          'Monday' =&gt; 'Monday',
                          'Tuesday' =&gt; 'Tuesday',
                          'Wednesday' =&gt; 'Wednesday',
                          'Thursday' =&gt; 'Thursday',
                          'Friday' =&gt; 'Friday',
                          'Saturday' =&gt; 'Saturday',
                          'Sunday' =&gt; 'Sunday',
                      );
                  break;
  
                  case 'Month':
                      $choicesOn = array(
                          '1' =&gt; '1',
                          '2' =&gt; '2',
                          '3' =&gt; '3',
                          '4' =&gt; '4',
                          '5' =&gt; '5',
                          '6' =&gt; '6',
                          '7' =&gt; '7',
                      );
                  break;
  
                  default:
                      $choicesOn = array();
                  break;
              }
  
              $form-&gt;add('on', ChoiceType::class, array(
                  'choices'     =&gt; $choicesOn,
              ));
  
          };
          $builder-&gt;addEventListener(
              FormEvents::PRE_SET_DATA,
              function (FormEvent $event) use ($addFrequencyOnField) {
                  $every = $event-&gt;getData()-&gt;getEvery();
                  $every_val = $every ? $every-&gt;getEvery() : null;
                  $addFrequencyOnField($event-&gt;getForm(), $every_val);
              }
          );
          $builder-&gt;addEventListener(
              FormEvents::PRE_SUBMIT,
              function (FormEvent $event) use ($addFrequencyOnField) {
                  $data = $event-&gt;getData();
                  $every_val = array_key_exists('every', $data) ? $data['every'] : null;
                  $addFrequencyOnField($event-&gt;getForm(), $every_val);
              }
          );
      }
      /**
       * {@inheritdoc}
       */
      public function configureOptions(OptionsResolver $resolver)
      {
          $resolver-&gt;setDefaults(array(
              'data_class' =&gt; 'AppBundle\Entity\Projects'
          ));
      }
  
      /**
       * {@inheritdoc}
       */
      public function getBlockPrefix()
      {
          return 'appbundle_projects';
      }
  }
  </code></pre>
  <p>and finally, the <strong>Edit Template</strong> containing ajax</p>
  <pre><code>{% extends 'base.html.twig' %}
  {% block body %}
      {% block content %}
        &lt;h1&gt;Edit Project&lt;/h1&gt;
  
        {{ form_start(edit_form) }}
            {{ form_widget(edit_form) }}
            &lt;input type=&quot;submit&quot; value=&quot;Edit&quot; /&gt;
        {{ form_end(edit_form) }}
      {% endblock content %}
  
  
      {% block javascripts %}
          &lt;script&gt;
            var $update =       $('#appbundle_projects_update');
            var $every =        $('#appbundle_projects_every');
            var $on_selector =  $('#appbundle_projects_on');
  
            // When update gets selected ...
            $update.change(function() {
              $every.html('');
              // ... retrieve the corresponding form.
              var $form = $(this).closest('form');
              // Simulate form data, but only include the selected update value.
              var data = {};
              data[$update.attr('name')] = $update.val();
              // Submit data via AJAX to the form's action path.
              $.ajax({
                url : $form.attr('action'),
                type: $form.attr('method'),
                data : data,
                success: function(html) {
                  var options = $(html).find('#appbundle_projects_every option');
                  for (var i=0, total = options.length; i &lt; total; i++) {
                      $every.append(options[i]);
                  }
                }
              });
            });
            // // When every gets selected ...
            $every.change(function() {
              $on_selector.html('');
              // ... retrieve the corresponding form.
              var $form = $(this).closest('form');
              // Simulate form data, but only include the selected every value.
              var data = {};
              data[$every.attr('name')] = $every.val();
              // Submit data via AJAX to the form's action path.
              $.ajax({
                url : $form.attr('action'),
                type: $form.attr('method'),
                data : data,
                success: function(html) {
                    var options = $(html).find('#appbundle_projects_on option');
                    for (var i=0, total = options.length; i &lt; total; i++) {
                        $on_selector.append(options[i]);
                    }
                }
              });
            });
          &lt;/script&gt;
      {% endblock javascripts %}
  {% endblock %}
  </code></pre>
  <p>I was able to set the selected value in the update field using <code>setUpdateChoice()</code> and assigning the return value to the <code>ChoiceType::class</code> <code>data</code> in ProjectsType. However, this doesn't have any effect on the <code>every select</code> or the other one, the <code>PRE_SET_DATA</code> listener returning <code>null</code>.</p>
  <p>I've tried the same as above with the other two fields as well, but having the fields rendered in the listener resulted in an error <code>no such field</code> (or something similar)</p>
  <p>I've also tried to get the value from the frequency in the controller and using <code>setUpdate()</code>, <code>setEvery()</code> and <code>setOn()</code> before rendering the form, resulting in <code>You can't modify a form that has already been submitted.</code></p>
  <p>This is how the form currently renders when editing a project:</p>
  <p><a href="https://i.stack.imgur.com/N6yNO.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/N6yNO.png" alt="initial render" /></a></p>
  <p>And I would need it populated like this:</p>
  <p><a href="https://i.stack.imgur.com/Olmju.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/Olmju.png" alt="enter image description here" /></a></p>
  <p>Any idea/advice/example of how I can tackle this would be greatly appreciated.</p>
  <p>Thank you</p>
  <p><em>PS. If I've omitted something, please let me know and I'll update my question.</em></p>
  <h3>Update</h3>
  <p>Thanks to <strong>Constantin's</strong> answer, I've managed to set the fields data in the controller doing the following:</p>
  <pre><code>  public function editAction(Request $request, Projects $project)
    {
        $project-&gt;setUpdate($project-&gt;getFrequency()['update']);
        $project-&gt;setEvery($project-&gt;getFrequency()['every']);
        $project-&gt;setOn($project-&gt;getFrequency()['on']);
  
        $editForm = $this-&gt;createForm('AppBundle\Form\ProjectsType', $project);
        $editForm-&gt;handleRequest($request);
  
        if ($editForm-&gt;isSubmitted() &amp;&amp; $editForm-&gt;isValid()) {
            $frequency = array(
                &quot;update&quot; =&gt; $project-&gt;getUpdate(),
                &quot;every&quot; =&gt; $project-&gt;getEvery(),
                &quot;on&quot; =&gt; $project-&gt;getOn()
            );
  
  
            $project-&gt;setFrequency($frequency);
            $this-&gt;getDoctrine()-&gt;getManager()-&gt;flush();
  
            return $this-&gt;redirectToRoute('projects_edit', array('id' =&gt; $project-&gt;getId()));
        }
  
        return $this-&gt;render('projects/edit.html.twig', array(
            'project' =&gt; $project,
            'edit_form' =&gt; $editForm-&gt;createView(),
            'delete_form' =&gt; $deleteForm-&gt;createView(),
        ));
    }
  </code></pre>