I would like to create a fieldset under each radio/checkbox item.
e.g
Which animals do you like:
[x] Cats
[Fieldset of cat related questions]
[ ] Dogs
[Fieldset of dog related questions]
...
I can create Fieldsets and Forms with ease, but nesting them under each item is causing me some headaches.
Ultimately I had in mind something like this:
$this->add(array(
'type' => 'Zend\Form\Element\Radio',
'name' => 'profile_type',
'options' => array(
'label' => 'Which animals do you like:',
'label_attributes' => array('class'=>'radio inline'),
'value_options' => array(
'1' =>'cats',
'fieldsets' => array(
array(
'name' => 'sender',
'elements' => array(
array(
'name' => 'name',
'options' => array(
'label' => 'Your name',
),
'type' => 'Text'
),
array(
'type' => 'Zend\Form\Element\Email',
'name' => 'email',
'options' => array(
'label' => 'Your email address',
),
),
),
)),
'2'=>'dogs',
'fieldsets' => array(
array(
'name' => 'sender',
'elements' => array(
array(
'name' => 'name',
'options' => array(
'label' => 'Your name',
),
'type' => 'Text'
),
array(
'type' => 'Zend\Form\Element\Email',
'name' => 'email',
'options' => array(
'label' => 'Your email address',
),
),
),
))
),
),
'attributes' => array(
'value' => '1' //set checked to '1'
)
));
Ok I managed to get round this issue using a bit of a hack, but it works.
TestForm.php (note the additional attributes in the elements ('parent' and 'childOf')
class TestForm extends Form
{
public $user_agent;
public $user_ip;
public function __construct($name = null)
{
// we want to ignore the name passed
parent::__construct('profile');
$this->setAttributes(array(
'method'=>'post',
'class'=>'form-horizontal',
));
$this->add(array(
'type' => 'Zend\Form\Element\Radio',
'name' => 'profile_type',
'options' => array(
'label' => 'Which animals do you like:',
'label_attributes' => array('class'=>'radio inline'),
'value_options' => array(
array('label' =>'cats', 'value'=>1, 'parent'=>'cat'),
array('label'=>'dogs', 'value'=>2, 'parent'=>'dog'),
),
),
'attributes' => array(
'value' => '1' //set checked to '1'
)
));
$this->add(array(
'type' => 'Text',
'name' => 'name',
'attributes' => array(
'childOf' => 'cat',
),
'options' => array(
'label' => 'Your name',
),
));
$this->add(array(
'type' => 'Zend\Form\Element\Email',
'name' => 'email',
'attributes' => array(
'childOf' => 'dog',
),
'options' => array(
'label' => 'Your email address',
),
));
}
}
Then extended Zend\Form\View\Helper\FormMultiCheckbox and overwrote the RenderOptions Method (look out for the new optionSpec 'parent') this basically creates a tag e.g.{#cat#} after the parent element:
protected function renderOptions(MultiCheckboxElement $element, array $options, array $selectedOptions,
array $attributes)
{
$escapeHtmlHelper = $this->getEscapeHtmlHelper();
$labelHelper = $this->getLabelHelper();
$labelClose = $labelHelper->closeTag();
$labelPosition = $this->getLabelPosition();
$globalLabelAttributes = $element->getLabelAttributes();
$closingBracket = $this->getInlineClosingBracket();
if (empty($globalLabelAttributes)) {
$globalLabelAttributes = $this->labelAttributes;
}
$combinedMarkup = array();
$count = 0;
foreach ($options as $key => $optionSpec) {
$count++;
if ($count > 1 && array_key_exists('id', $attributes)) {
unset($attributes['id']);
}
$value = '';
$parent = '';
$label = '';
$inputAttributes = $attributes;
$labelAttributes = $globalLabelAttributes;
$selected = isset($inputAttributes['selected']) && $inputAttributes['type'] != 'radio' && $inputAttributes['selected'] != false ? true : false;
$disabled = isset($inputAttributes['disabled']) && $inputAttributes['disabled'] != false ? true : false;
if (is_scalar($optionSpec)) {
$optionSpec = array(
'label' => $optionSpec,
'value' => $key
);
}
if (isset($optionSpec['value'])) {
$value = $optionSpec['value'];
}
if (isset($optionSpec['parent'])) {
$parent = $optionSpec['parent'];
}
if (isset($optionSpec['label'])) {
$label = $optionSpec['label'];
}
if (isset($optionSpec['selected'])) {
$selected = $optionSpec['selected'];
}
if (isset($optionSpec['disabled'])) {
$disabled = $optionSpec['disabled'];
}
if (isset($optionSpec['label_attributes'])) {
$labelAttributes = (isset($labelAttributes))
? array_merge($labelAttributes, $optionSpec['label_attributes'])
: $optionSpec['label_attributes'];
}
if (isset($optionSpec['attributes'])) {
$inputAttributes = array_merge($inputAttributes, $optionSpec['attributes']);
}
if (in_array($value, $selectedOptions)) {
$selected = true;
}
$inputAttributes['value'] = $value;
$inputAttributes['checked'] = $selected;
$inputAttributes['disabled'] = $disabled;
$input = sprintf(
'<input %s%s',
$this->createAttributesString($inputAttributes),
$closingBracket
);
if (null !== ($translator = $this->getTranslator())) {
$label = $translator->translate(
$label, $this->getTranslatorTextDomain()
);
}
$tag = ($parent != '')? "{#*".$parent."*#}": "";
$label = $escapeHtmlHelper($label);
$labelOpen = $labelHelper->openTag($labelAttributes);
$template = $labelOpen . '%s%s%s' . $labelClose;
switch ($labelPosition) {
case self::LABEL_PREPEND:
$markup = sprintf($template, $label, $input, $tag);
break;
case self::LABEL_APPEND:
default:
$markup = sprintf($template, $input, $label, $tag);
break;
}
$combinedMarkup[] = $markup;
}
return implode($this->getSeparator(), $combinedMarkup);
}
Extended and overwrote Zend\Form\View\Helper\FormRow render method this is the same except for the return of the method:
..... more code .....
$child_of = $element->getAttribute('childOf');
if($child_of != '')
{
return array($child_of => sprintf('<div class="control-group%s">%s</div>', $status_type, $markup));
}
return sprintf('<div class="control-group%s">%s</div>', $status_type, $markup);
And finally extended and overwrote the render method of Zend\Form\View\Helper\FormCollection and changed the element foreach loop, basically overwriting the tags if the element is an array, and therefore has a ChildOf tag. Then a clean up of tags:
foreach ($element->getIterator() as $elementOrFieldset) {
if ($elementOrFieldset instanceof FieldsetInterface) {
$markup .= $fieldsetHelper($elementOrFieldset);
} elseif ($elementOrFieldset instanceof ElementInterface) {
$elementString = $elementHelper($elementOrFieldset);
if(!is_array($elementString))
{
$markup .= $elementString;
}
// is child of another element
else
{
foreach($elementString as $key => $value)
{
$match = "{#*".$key."*#}";
$replacement = $value.$match;
$markup = str_replace($match, $replacement, $markup);
}
}
}
}
$pattern = '/[{#\*]+[a-z0-0A-Z]*[\*#}]+/';
$markup = preg_replace($pattern, '', $markup);
This (although ugly) produces the desired results, also because we are just playing with the rendering, validation and form creation are untouched.
All the best, Aborgrove