Question

This is my simplified problem description. I have several tables with similar columns which contain different data:

table_one.id, table_one.name, ..., table_one.foo
table_two.id, table_two.name, ..., table_two.foo

table_one.foo allowed values are 'a', 'b', 'c',

table_two.foo allowed values are 'x', 'y', 'z'.

In the view I have a form that allows to enter several records into table_one and table_two:

$this->Form->input('TableOne.0.foo', array('type' => 'select'));
$this->Form->input('TableOne.1.foo', array('type' => 'select'));
$this->Form->input('TableOne.2.foo', array('type' => 'select'));
...
$this->Form->input('TableTwo.0.foo', array('type' => 'select'));
$this->Form->input('TableTwo.1.foo', array('type' => 'select'));
$this->Form->input('TableTwo.2.foo', array('type' => 'select'));

In the controller I construct the lists and pass them to the view:

$tableOneFooList = array('a', 'b', 'c');
$tableTwoFooList = array('x', 'y', 'z');
$this->set('foo', $tableOneFooList);

The problem with that is that I cannot set the second foo variable and $tableOneFooList is populated into all 6 select boxes. I could name the lists differently in the controller but that would require more work in the view to select the right values after a failed validation. Is there a good way to pass the lists to the view so that if the form doesn't validate after the submission the selected values would be preserved? Maybe I do not know some naming convention?

Was it helpful?

Solution

As far as I know the form helper magic isn't able to distinguish such two fields, you'll either have to pass the lists separately, or use a custom form helper.

Using the options option

When passing the lists separately, all you have to do is using the options option, if necessary CakePHP will automatically select the appropriate list entries based on the values passed in the request.

Controller

$tableOneFoos = array('a', 'b', 'c');
$tableTwoFoos = array('x', 'y', 'z');
$this->set(compact('tableOneFoos', 'tableTwoFoos'));

View

$this->Form->input('TableOne.0.foo', array('type' => 'select', 'options' => $tableOneFoos));
$this->Form->input('TableOne.1.foo', array('type' => 'select', 'options' => $tableOneFoos));
$this->Form->input('TableOne.2.foo', array('type' => 'select', 'options' => $tableOneFoos));

$this->Form->input('TableTwo.0.foo', array('type' => 'select', 'options' => $tableTwoFoos));
$this->Form->input('TableTwo.1.foo', array('type' => 'select', 'options' => $tableTwoFoos));
$this->Form->input('TableTwo.2.foo', array('type' => 'select', 'options' => $tableTwoFoos));

That's it, the values should be selected as expected when rendering the form with the submitted data.

Custom form helper

The form helper checks for variable names that are camelCased plurals of the fieldname (see FormHelper::_optionsOptions()), the model name isn't taken into account, and so in your case it will look for foos, and that's why you end with this one list being used for all inputs.

You can override that method and implement an additional check that uses the model name, so that the helper would look for variables like tableOneFoos and tableTwoFoos.

Here's an untested! example:

App::uses('FormHelper', 'View/Helper');

class MyFormHelper extends FormHelper {
    protected function _optionsOptions($options) {
        $options = parent::_optionsOptions($options);
        
        if (!isset($options['options'])) {
            // this is where the magic happens, an entity name like
            // `Model_field` is turned into a variable name like
            // `modelFields` which is then used to lookup the view vars.
            $entityName = $this->model() . '_' . $this->field();
            $varName = Inflector::variable(
                Inflector::pluralize(preg_replace('/_id$/', '', $entityName))
            );
            $varOptions = $this->_View->get($varName);

            if (is_array($varOptions)) {
                if ($options['type'] !== 'radio') {
                    $options['type'] = 'select';
                }
                $options['options'] = $varOptions;
            }
        }

        return $options;
    }
}

Then you could just pass tableOneFoos and tableTwoFoos to the view without using the options and type options for the inputs:

Controller

$tableOneFoos = array('a', 'b', 'c');
$tableTwoFoos = array('x', 'y', 'z');
$this->set(compact('tableOneFoos', 'tableTwoFoos'));

View

$this->MyForm->input('TableOne.0.foo');
$this->MyForm->input('TableOne.1.foo');
$this->MyForm->input('TableOne.2.foo');

$this->MyForm->input('TableTwo.0.foo');
$this->MyForm->input('TableTwo.1.foo');
$this->MyForm->input('TableTwo.2.foo');

This should work as long as there are no TableOneFoo or TableTwoFoo models used in a HABTM fashion way, as their fields would end up with the same variable names, ie tableOneFoos and tableTwoFoos.

OTHER TIPS

Lets not make this complex-- Simple solution would be-

<?php
$tableOneFooList = array('a', 'b', 'c');
$tableTwoFooList = array('x', 'y', 'z');

echo $this->Form->create();
echo $this->Form->input('TableOne.0.foo', array('type' => 'select', 'options' => $tableOneFooList));
echo $this->Form->input('TableOne.1.foo', array('type' => 'select', 'options' => $tableOneFooList));
echo $this->Form->input('TableOne.2.foo', array('type' => 'select', 'options' => $tableOneFooList));
echo $this->Form->input('TableTwo.0.foo', array('type' => 'select', 'options' => $tableTwoFooList));
echo $this->Form->input('TableTwo.1.foo', array('type' => 'select', 'options' => $tableTwoFooList));
echo $this->Form->input('TableTwo.2.foo', array('type' => 'select', 'options' => $tableTwoFooList));
echo $this->Form->end('Submit');
?>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top