Search code examples
postsymfony1symfony-1.4

Can't get extra form fields when the form is submitted


My automatic generated base form class is as follows:

abstract class BaseGestorForm extends BaseFormDoctrine {
    public function setup() {
        $this->setWidgets(array(
            'persona_fk' => new sfWidgetFormInputHidden(),
            'unitat_fk'  => new sfWidgetFormInputHidden(),
            'baixa'      => new sfWidgetFormDateTime(),
        ));
        $this->setValidators(array(
            'persona_fk' => new sfValidatorChoice(array('choices' => array($this->getObject()->get('persona_fk')), 'empty_value' => $this->getObject()->get('persona_fk'), 'required' => false)),
            'unitat_fk'  => new sfValidatorChoice(array('choices' => array($this->getObject()->get('unitat_fk')), 'empty_value' => $this->getObject()->get('unitat_fk'), 'required' => false)),
            'baixa'      => new sfValidatorDateTime(array('required' => false)),
            ));  
        $this->widgetSchema->setNameFormat('gestor[%s]');
        $this->errorSchema = new sfValidatorErrorSchema($this->validatorSchema);   
        $this->setupInheritance();
        parent::setup();
    }

    public function getModelName() {
        return 'Gestor';
    }
}    

In the form I added two extra fields (totes_unitats and unitats_a_gestionar). The first field is a drop down list where users select one or more choices and using jquery when a user press a button the options selected are added to unitats_a_gestionar drop down list. At the same time, these options are removed from totes_unitats list.

class GestorForm extends BaseGestorForm {   
    public function configure() {
        unset($this['baixa']);

        $this->widgetSchema['persona_fk'] = new sfWidgetFormChoice(array(
            'choices' => UsuariLdap::getAllUsuaris()
        ));

        $this->widgetSchema['totes_unitats'] = new sfWidgetFormChoice(array(
            'choices' => UnitatTable::findAllUnitatsPerOrdreArray(),
            'multiple' => true
        ));

        $this->widgetSchema['unitats_a_gestionar'] = new sfWidgetFormChoice(array(
            'choices' => array(),
            'multiple' => true
        ));

        $this->widgetSchema->setLabels(array(
            'persona_fk' => 'Gestor',
            'unitat_fk' => 'Unitat',
            'totes_unitats' => 'Totes les unitats',
            'unitats_a_gestionar' => 'Unitats a gestionar'
        ));

        $this->validatorSchema->setOption('allow_extra_fields', true);

        $this->validatorSchema['persona_fk'] = new sfValidatorString(array('required' => true), array('required' => 'Requerit'));
    }   
}    

Where I find the problem is in the actions file. First of all, I call the executeNouGestor method that renders the form. Then when the user press to proceed and create the Gestor object, it calls executeValidaGestor that validates the form. This last method calls processFormGestor where there is no way to retrieve the unitats_a_gestionar extra field.

public function executeNouGestor(sfWebRequest $request) {
    $this->gestorForm = new GestorForm();
}

public function executeValidaGestor(sfWebRequest $request) {
    $this->forward404Unless($request->isMethod(sfRequest::POST));
    $this->gestorForm = new GestorForm();   
    $this->processFormGestor($request, $this->gestorForm);
    $this->setTemplate('nouGestor');
}

protected function processFormGestor(sfWebRequest $request, sfForm $gestorForm) {
    $gestorForm->bind($request->getParameter($gestorForm->getName()), $request->getFiles($gestorForm->getName()));
    if ($gestorForm->isValid()) { 
        var_dump($_POST);
        var_dump($request->getParameterHolder()->getAll());
        ...
    }
}

These two var_dump shows me the following information:

var_dump($_POST):

array(2) {
  ["gestor"]=>
  array(2) {
    ["persona_fk"]=>
    string(3) "330"
    ["_csrf_token"]=>
    string(32) "91e18aa0570bfc7558d21ebb4b98f512"
  }
  ["Desar"]=>
  string(5) "Desar"
}

var_dump($request->getParameterHolder()->getAll()):

array(4) {
  ["gestor"]=>
  array(2) {
    ["persona_fk"]=>
    string(3) "330"
    ["_csrf_token"]=>
    string(32) "91e18aa0570bfc7558d21ebb4b98f512"
  }
  ["Desar"]=>
  string(5) "Desar"
  ["module"]=>
  string(13) "administracio"
  ["action"]=>
  string(12) "validaGestor"
}

So, as you can see, in ["gestor"] there is no track neither totes_unitats nor unitats_a_gestionar extra form fields. I have no idea why. The way I show the form fields in the template is as usual:

<?php echo $gestorForm['persona_fk']->renderLabel(); ?>
<div class="input"><?php echo $gestorForm['persona_fk']->render(); ?></div>
<div class="error-input"><?php echo $gestorForm['persona_fk']->renderError(); ?></div>

<?php echo $gestorForm['totes_unitats']->renderLabel(); ?>
<div class="input multiple"><?php echo $gestorForm['totes_unitats']->render(); ?></div>
<div class="error-input"><?php echo $gestorForm['totes_unitats']->renderError(); ?></div>

<?php echo $gestorForm['unitats_a_gestionar']->renderLabel(); ?>
<div class="input multiple"><?php echo $gestorForm['unitats_a_gestionar']->render(); ?></div>
<div class="error-input"><?php echo $gestorForm['unitats_a_gestionar']->renderError(); ?></div>

I also add the jquery code that manage the options added or removed between the two drop down lists with multiple selection:

function afegirTreureUnitats() {
  var boto_afegir = $("#btn-multiple-afegir");
  var boto_treure = $("#btn-multiple-treure");
  boto_afegir.click(function() {
    var selectedItems = $("#gestor_totes_unitats option:selected");
    var output = [];
    $.each(selectedItems, function(key, e)
    {
      output.push('<option value="' + e.value + '">' + e.text + '</option>');

    });
    $("#gestor_unitats_a_gestionar").append(output.join(""));
    ordenaOptionsSelect("gestor_unitats_a_gestionar");
    selectedItems.remove();
  });  
  boto_treure.click(function() {
    var selectedItems = $("#gestor_unitats_a_gestionar option:selected");
    var output = [];
    $.each(selectedItems, function(key, e)
    {
      output.push('<option value="' + e.value + '">' + e.text + '</option>');
    });
    $("#gestor_totes_unitats").append(output.join(""));
    ordenaOptionsSelect("gestor_totes_unitats");
    selectedItems.remove();
  });
}

function ordenaOptionsSelect(idSelect)
{
  var options = $('#' + idSelect + ' option');
  options.sort(function(a, b)
  {
    if (a.text > b.text)
      return 1;
    else if (a.text < b.text)
      return -1;
    else
      return 0;
  });
  $('#' + idSelect).empty().append(options);
}

$(document).ready(function() {
  afegirTreureUnitats();
});

The form rendered has this appearance:

https://drive.google.com/file/d/0B0Mz720p9Q_DN1RnYWIyR0pXOTQ/edit?usp=sharing

I have also found a curious fact. If I select one of the options in the drop down list, either totes_unitats or unitats_a_gestionar they are sent through POST method, but only one (if I select more than one I only can get one of the options selected).


Solution

  • When you use <select> elements on the form, or <input> of type radio or checkbox the browser will send their values on form submission only when the fields have any options selected.

    The list of options in the <select> tag is not sent back to the server. Only the values of the options which are actually selected.

    You can resolve your problem in two ways:

    1. Either create a JS which will modify your form just before sending it and will select all the items on both your lists. This way your form will send the values and you will be able to work with them on the server side.

    2. Other option is to add two hidden fields which will keep the lists of the options and modify these lists together with the <select> fields.