Permalink
Browse files

Refactoring `Form` helper string templates to use wrapping instead of…

… start/end elements. Fixing type-checking in `\util\String::insert()`. Adding sanity checking in `\template\Helper::_attributes()`.
  • Loading branch information...
1 parent 8c4078a commit 0a20c0b931c875e467ea1a41effbc767014a82d9 @nateabele nateabele committed Jul 28, 2011
View
@@ -158,15 +158,17 @@ protected function _render($method, $string, $params, array $options = array())
* @return string
*/
protected function _attributes($params, $method = null, array $options = array()) {
- if (!is_array($params)) {
- return !$params ? '' : ' ' . $params;
- }
$defaults = array('escape' => true, 'prepend' => ' ', 'append' => '');
$options += $defaults;
$result = array();
+ if (!is_array($params)) {
+ return !$params ? '' : $options['prepend'] . $params;
+ }
foreach ($params as $key => $value) {
- $result[] = $this->_attribute($key, $value, $options);
+ if ($next = $this->_attribute($key, $value, $options)) {
+ $result[] = $next;
+ }
}
return $result ? $options['prepend'] . implode(' ', $result) . $options['append'] : '';
}
View
@@ -37,39 +37,34 @@ class Form extends \lithium\template\Helper {
* @var array
*/
protected $_strings = array(
- 'button' => '<button{:options}>{:name}</button>',
- 'checkbox' => '<input type="checkbox" name="{:name}"{:options} />',
- 'checkbox-multi' => '<input type="checkbox" name="{:name}[]"{:options} />',
- 'checkbox-multi-end' => '',
- 'checkbox-multi-start' => '',
- 'error' => '<div{:options}>{:content}</div>',
- 'errors' => '{:content}',
- 'element' => '<input type="{:type}" name="{:name}"{:options} />',
- 'file' => '<input type="file" name="{:name}"{:options} />',
- 'form' => '<form action="{:url}"{:options}>{:append}',
- 'form-end' => '</form>',
- 'hidden' => '<input type="hidden" name="{:name}"{:options} />',
- 'field' => '<div{:wrap}>{:label}{:input}{:error}</div>',
- 'field-checkbox' => '<div{:wrap}>{:input}{:label}{:error}</div>',
- 'field-radio' => '<div{:wrap}>{:input}{:label}{:error}</div>',
- 'label' => '<label for="{:name}"{:options}>{:title}</label>',
- 'legend' => '<legend>{:content}</legend>',
- 'option-group' => '<optgroup label="{:label}"{:options}>',
- 'option-group-end' => '</optgroup>',
- 'password' => '<input type="password" name="{:name}"{:options} />',
- 'radio' => '<input type="radio" name="{:name}" {:options} />',
- 'select-start' => '<select name="{:name}"{:options}>',
- 'select-multi-start' => '<select name="{:name}[]"{:options}>',
- 'select-empty' => '<option value=""{:options}>&nbsp;</option>',
- 'select-option' => '<option value="{:value}"{:options}>{:title}</option>',
- 'select-end' => '</select>',
- 'submit' => '<input type="submit" value="{:title}"{:options} />',
- 'submit-image' => '<input type="image" src="{:url}"{:options} />',
- 'text' => '<input type="text" name="{:name}"{:options} />',
- 'textarea' => '<textarea name="{:name}"{:options}>{:value}</textarea>',
- 'fieldset' => '<fieldset{:options}>{:content}</fieldset>',
- 'fieldset-start' => '<fieldset><legend>{:content}</legend>',
- 'fieldset-end' => '</fieldset>'
+ 'button' => '<button{:options}>{:name}</button>',
+ 'checkbox' => '<input type="checkbox" name="{:name}"{:options} />',
+ 'checkbox-multi' => '<input type="checkbox" name="{:name}[]"{:options} />',
+ 'checkbox-multi-group' => '{:raw}',
+ 'error' => '<div{:options}>{:content}</div>',
+ 'errors' => '{:raw}',
+ 'input' => '<input type="{:type}" name="{:name}"{:options} />',
+ 'file' => '<input type="file" name="{:name}"{:options} />',
+ 'form' => '<form action="{:url}"{:options}>{:append}',
+ 'form-end' => '</form>',
+ 'hidden' => '<input type="hidden" name="{:name}"{:options} />',
+ 'field' => '<div{:wrap}>{:label}{:input}{:error}</div>',
+ 'field-checkbox' => '<div{:wrap}>{:input}{:label}{:error}</div>',
+ 'field-radio' => '<div{:wrap}>{:input}{:label}{:error}</div>',
+ 'label' => '<label for="{:name}"{:options}>{:title}</label>',
+ 'legend' => '<legend>{:content}</legend>',
+ 'option-group' => '<optgroup label="{:label}"{:options}>{:raw}</optgroup>',
+ 'password' => '<input type="password" name="{:name}"{:options} />',
+ 'radio' => '<input type="radio" name="{:name}" {:options} />',
+ 'select' => '<select name="{:name}"{:options}>{:raw}</select>',
+ 'select-empty' => '<option value=""{:options}>&nbsp;</option>',
+ 'select-multi' => '<select name="{:name}[]"{:options}>{:raw}</select>',
+ 'select-option' => '<option value="{:value}"{:options}>{:title}</option>',
+ 'submit' => '<input type="submit" value="{:title}"{:options} />',
+ 'submit-image' => '<input type="image" src="{:url}"{:options} />',
+ 'text' => '<input type="text" name="{:name}"{:options} />',
+ 'textarea' => '<textarea name="{:name}"{:options}>{:value}</textarea>',
+ 'fieldset' => '<fieldset{:options}><legend>{:content}</legend>{:raw}</fieldset>',
);
/**
@@ -351,7 +346,7 @@ public function __call($type, array $params = array()) {
$params += array(null, array());
list($name, $options) = $params;
list($name, $options, $template) = $this->_defaults($type, $name, $options);
- $template = $this->_context->strings($template) ? $template : 'element';
+ $template = $this->_context->strings($template) ? $template : 'input';
return $this->_render($type, $template, compact('type', 'name', 'options', 'value'));
}
@@ -528,40 +523,48 @@ public function select($name, $list = array(), array $options = array()) {
if ($scope['empty']) {
$list = array('' => ($scope['empty'] === true) ? '' : $scope['empty']) + $list;
}
- $startTemplate = ($scope['multiple']) ? 'select-multi-start' : 'select-start';
- $output = $this->_render(__METHOD__, $startTemplate, compact('name', 'options'));
-
- $parseOptions = function($self, $method, $list) use (&$scope, &$parseOptions) {
- $output = "";
- foreach($list as $value => $title) {
- if(is_array($title)) {
- $label = $value;
- $options = array();
- $output .= $self->invokeMethod('_render', array($method, 'option-group', compact('label', 'options')));
- $output .= $parseOptions($self, $method, $title);
- $output .= $self->invokeMethod('_render', array($method, 'option-group-end', array()));
- } else {
- $selected = false;
-
- if (is_array($scope['value']) && in_array($value, $scope['value'])) {
- $selected = true;
- } elseif ($scope['value'] == $value) {
- $selected = true;
- }
- $options = $selected ? array('selected' => true) : array();
-
- $output .= $self->invokeMethod('_render', array(
- $method,
- 'select-option',
- compact('value', 'title', 'options')
- ));
- }
+ if ($template == __FUNCTION__ && $scope['multiple']) {
+ $template = 'select-multi';
+ }
+ $raw = $this->_selectOptions($list, $scope);
+ return $this->_render(__METHOD__, $template, compact('name', 'options', 'raw'));
+ }
+
+ /**
+ * Generator method used by `select()` to produce `<option />` and `<optgroup />` elements.
+ * Generally, this method should not need to be called directly, but through `select()`.
+ *
+ * @param array $list Either a flat key/value array of select menu options, or an array which
+ * contains key/value elements and/or elements where the keys are `<optgroup />`
+ * titles and the values are sub-arrays of key/value pairs representing nested
+ * `<option />` elements.
+ * @param array $scope An array of options passed to the parent scope, including the currently
+ * selected value of the associated form element.
+ * @return string Returns a string of `<option />` and (optionally) `<optgroup />` tags to be
+ * embedded in a select element.
+ */
+ protected function _selectOptions(array $list, array $scope) {
+ $result = "";
+
+ foreach ($list as $value => $title) {
+ if (is_array($title)) {
+ $label = $value;
+ $options = array();
+
+ $raw = $this->_selectOptions($title, $scope);
+ $params = compact('label', 'options', 'raw');
+ $result .= $this->_render('select', 'option-group', $params);
+ continue;
}
- return $output;
- };
- $output .= $parseOptions($this, __METHOD__, $list);
-
- return $output . $this->_context->strings('select-end');
+ $selected = (
+ (is_array($scope['value']) && in_array($value, $scope['value'])) ||
+ ($scope['value'] == $value)
+ );
+ $options = $selected ? array('selected' => true) : array();
+ $params = compact('value', 'title', 'options');
+ $result .= $this->_render('select', 'select-option', $params);
+ }
+ return $result;
}
/**
@@ -100,7 +100,7 @@ public function testAttributes() {
$attributes = array('checked' => false);
$result = $this->helper->testAttributes($attributes);
- $this->assertEqual(' ', $result);
+ $this->assertEqual('', $result);
}
public function testAttributeEscaping() {
@@ -466,6 +466,7 @@ public function testCustomValueCheckbox() {
public function testSelectGeneration() {
$result = $this->form->select('foo');
+
$this->assertTags($result, array(
'select' => array('name' => 'foo', 'id' => 'Foo'), '/select'
));
@@ -692,7 +693,7 @@ public function testFormField() {
$expected = array(
'<div>',
'<input type="hidden" name="name" value="" />',
- '<input type="checkbox" name="name" id="Name" value="1" />',
+ '<input type="checkbox" name="name" id="Name" value="1" />',
'<label for="Name">Name</label></div>'
);
$this->assertEqual(join('', $expected), $result);
View
@@ -239,7 +239,15 @@ public static function insert($str, array $data, array $options = array()) {
$replace = array();
foreach ($data as $key => $value) {
- $value = is_array($value) ? '' : (string) $value;
+ $value = (is_array($value) || $value instanceof Closure) ? '' : $value;
+
+ try {
+ if (is_object($value) && method_exists($value, '__toString')) {
+ $value = (string) $value;
+ }
+ } catch (Exception $e) {
+ $value = '';
+ }
$replace["{$options['before']}{$key}{$options['after']}"] = $value;
}
$str = strtr($str, $replace);
@@ -264,14 +272,6 @@ public static function insert($str, array $data, array $options = array()) {
continue;
}
$str = preg_replace($key, $hashVal, $str);
-
- if (is_object($value) && !$value instanceof Closure) {
- try {
- $value = $value->__toString();
- } catch (Exception $e) {
- $value = '';
- }
- }
$str = str_replace($hashVal, $value, $str);
}

0 comments on commit 0a20c0b

Please sign in to comment.