/**
* Generates a grouped HTML selection list from nested arrays.
*
* @param array $data An array of groups, each of which is an array of options.
* @param string $name The value of the HTML name attribute
* @param array $options Options, an array of key/value pairs. Valid options are:
* Format options, {@see HTMLHelper::$formatOptions}.
* Selection options. See {@see JHtmlSelect::options()}.
* group.id: The property in each group to use as the group id
* attribute. Defaults to none.
* group.label: The property in each group to use as the group
* label. Defaults to "text". If set to null, the data array index key is
* used.
* group.items: The property in each group to use as the array of
* items in the group. Defaults to "items". If set to null, group.id and
* group. label are forced to null and the data element is assumed to be a
* list of selections.
* id: Value to use as the select element id attribute. Defaults to
* the same as the name.
* list.attr: Attributes for the select element. Can be a string or
* an array of key/value pairs. Defaults to none.
* list.select: either the value of one selected option or an array
* of selected options. Default: none.
* list.translate: Boolean. If set, text and labels are translated via
* Text::_().
*
* @return string HTML for the select list
*
* @since 1.5
* @throws \RuntimeException If a group has contents that cannot be processed.
*/
public static function groupedlist($data, $name, $options = array())
{
// Set default options and overwrite with anything passed in
$options = array_merge(HTMLHelper::$formatOptions, array('format.depth' => 0, 'group.items' => 'items', 'group.label' => 'text', 'group.label.toHtml' => true, 'id' => false), $options);
// Apply option rules
if ($options['group.items'] === null) {
$options['group.label'] = null;
}
$attribs = '';
if (isset($options['list.attr'])) {
if (is_array($options['list.attr'])) {
$attribs = ArrayHelper::toString($options['list.attr']);
} else {
$attribs = $options['list.attr'];
}
if ($attribs !== '') {
$attribs = ' ' . $attribs;
}
}
$id = $options['id'] !== false ? $options['id'] : $name;
$id = str_replace(array('[', ']', ' '), '', $id);
// Disable groups in the options.
$options['groups'] = false;
$baseIndent = str_repeat($options['format.indent'], $options['format.depth']++);
$html = $baseIndent . '<select' . ($id !== '' ? ' id="' . $id . '"' : '') . ' name="' . $name . '"' . $attribs . '>' . $options['format.eol'];
$groupIndent = str_repeat($options['format.indent'], $options['format.depth']++);
foreach ($data as $dataKey => $group) {
$label = $dataKey;
$id = '';
$noGroup = is_int($dataKey);
if ($options['group.items'] == null) {
// Sub-list is an associative array
$subList = $group;
} elseif (is_array($group)) {
// Sub-list is in an element of an array.
$subList = $group[$options['group.items']];
if (isset($group[$options['group.label']])) {
$label = $group[$options['group.label']];
$noGroup = false;
}
if (isset($options['group.id']) && isset($group[$options['group.id']])) {
$id = $group[$options['group.id']];
$noGroup = false;
}
} elseif (is_object($group)) {
// Sub-list is in a property of an object
$subList = $group->{$options['group.items']};
if (isset($group->{$options['group.label']})) {
$label = $group->{$options['group.label']};
$noGroup = false;
}
if (isset($options['group.id']) && isset($group->{$options['group.id']})) {
$id = $group->{$options['group.id']};
$noGroup = false;
}
} else {
throw new \RuntimeException('Invalid group contents.', 1);
}
if ($noGroup) {
$html .= static::options($subList, $options);
} else {
$html .= $groupIndent . '<optgroup' . (empty($id) ? '' : ' id="' . $id . '"') . ' label="' . ($options['group.label.toHtml'] ? htmlspecialchars($label, ENT_COMPAT, 'UTF-8') : $label) . '">' . $options['format.eol'] . static::options($subList, $options) . $groupIndent . '</optgroup>' . $options['format.eol'];
}
}
$html .= $baseIndent . '</select>' . $options['format.eol'];
return $html;
}