Permalink
Browse files

Add complex optgroup type.

This adds support for a more complex type of optgroup elements.
  • Loading branch information...
1 parent f51f1ed commit 8e71c0ad9b4e8a6e3aa9c2dc4fc31e82f5512308 @markstory markstory committed Jan 8, 2014
Showing with 122 additions and 9 deletions.
  1. +82 −8 Cake/View/Input/SelectBox.php
  2. +40 −1 Test/TestCase/View/Input/SelectBoxTest.php
@@ -57,6 +57,59 @@ public function __construct($templates) {
* empty option.
* - `escape` - Set to false to disable HTML escaping.
*
+ * ### Options format
+ *
+ * The options option can take a variety of data format depending on
+ * the complexity of HTML you want generated.
+ *
+ * You can generate simple options using a basic associative array:
+ *
+ * {{{
+ * 'options' => ['elk' => 'Elk', 'beaver' => 'Beaver']
+ * }}}
+ *
+ * If you need to define additional attributes on your option elements
+ * you can use the complex form for options:
+ *
+ * {{{
+ * 'options' => [
+ * ['name' => 'elk', 'value' => 'Elk', 'data-foo' => 'bar'],
+ * ]
+ * }}}
+ *
+ * This form **requires** that both the `name` and `value` keys be defined.
+ * If either is not set options will not be generated correctly.
+ *
+ * If you need to define option groups you can do those using nested arrays:
+ *
+ * {{{
+ * 'options' => [
+ * 'Mammals' => [
+ * 'elk' => 'Elk',
+ * 'beaver' => 'Beaver'
+ * ]
+ * ]
+ * }}}
+ *
+ * And finally, if you need to put attributes on your optgroup elements you
+ * can do that with a more complex nested array form:
+ *
+ * {{{
+ * 'options' => [
+ * [
+ * 'name' => 'Mammals',
+ * 'data-id' => 1,
+ * 'options' => [
+ * 'elk' => 'Elk',
+ * 'beaver' => 'Beaver'
+ * ]
+ * ],
+ * ]
+ * }}}
+ *
+ * You are free to mix each of the forms in the same option set, and
+ * nest complex types as required.
+ *
* @param array $data Data to render with.
* @return string A generated select box.
* @throws \RuntimeException when the name attribute is empty.
@@ -96,7 +149,6 @@ public function render($data) {
* @return array
*/
protected function _renderContent($data) {
- $out = [];
$options = $data['options'];
if ($options instanceof Traversable) {
@@ -108,7 +160,7 @@ protected function _renderContent($data) {
$options = ['' => $value] + $options;
}
if (empty($options)) {
- return $out;
+ return [];
}
$selected = isset($data['value']) ? $data['value'] : null;
@@ -120,6 +172,28 @@ protected function _renderContent($data) {
}
/**
+ * Render the contents of an optgroup element.
+ *
+ * @param array $optgroup The opt group data.
+ */
+ protected function _renderOptgroup($label, $optgroup, $disabled, $selected, $escape) {
+ $opts = $optgroup;
+ $attrs = [];
+ if (isset($optgroup['options'], $optgroup['name'])) {
+ $opts = $optgroup['options'];
+ $label = $optgroup['name'];
+ $attrs = $optgroup;
+ }
+ $groupOptions = $this->_renderOptions($opts, $disabled, $selected, $escape);
+
+ return $this->_templates->format('optgroup', [
+ 'label' => $escape ? h($label) : $label,
+ 'content' => implode('', $groupOptions),
+ 'attrs' => $this->_templates->formatAttributes($attrs, ['name', 'options']),
+ ]);
+ }
+
+/**
* Render a set of options.
*
* Will recursively call itself when option groups are in use.
@@ -134,12 +208,12 @@ protected function _renderOptions($options, $disabled, $selected, $escape) {
$out = [];
foreach ($options as $key => $val) {
// Option groups
- if (!is_int($key) && is_array($val) || $val instanceof Traversable) {
- $groupOptions = $this->_renderOptions($val, $disabled, $selected, $escape);
- $out[] = $this->_templates->format('optgroup', [
- 'label' => $escape ? h($key) : $key,
- 'content' => implode('', $groupOptions)
- ]);
+ $arrayVal = (is_array($val) || $val instanceof Traversable);
+ if (
+ (!is_int($key) && $arrayVal) ||
+ (is_int($key) && $arrayVal && isset($val['options']))
+ ) {
+ $out[] = $this->_renderOptgroup($key, $val, $disabled, $selected, $escape);
continue;
}
@@ -28,7 +28,7 @@ public function setUp() {
$templates = [
'select' => '<select name="{{name}}"{{attrs}}>{{content}}</select>',
'option' => '<option value="{{name}}"{{attrs}}>{{value}}</option>',
- 'optgroup' => '<optgroup label="{{label}}">{{content}}</optgroup>',
+ 'optgroup' => '<optgroup label="{{label}}"{{attrs}}>{{content}}</optgroup>',
];
$this->templates = new StringTemplate();
$this->templates->add($templates);
@@ -281,6 +281,45 @@ public function testRenderOptionGroups() {
}
/**
+ * test rendering with option groups
+ *
+ * @return void
+ */
+ public function testRenderOptionGroupsWithAttributes() {
+ $select = new SelectBox($this->templates);
+ $data = [
+ 'name' => 'Birds[name]',
+ 'options' => [
+ [
+ 'name' => 'Mammal',
+ 'data-foo' => 'bar',
+ 'options' => [
+ 'beaver' => 'Beaver',
+ 'elk' => 'Elk',
+ ]
+ ]
+ ]
+ ];
+ $result = $select->render($data);
+ $expected = [
+ 'select' => [
+ 'name' => 'Birds[name]',
+ ],
+ ['optgroup' => ['data-foo' => 'bar', 'label' => 'Mammal']],
+ ['option' => ['value' => 'beaver']],
+ 'Beaver',
+ '/option',
+ ['option' => ['value' => 'elk']],
+ 'Elk',
+ '/option',
+ '/optgroup',
+ '/select'
+ ];
+ $this->assertTags($result, $expected);
+ }
+
+
+/**
* test rendering with option groups with traversable nodes
*
* @return void

0 comments on commit 8e71c0a

Please sign in to comment.