Skip to content

Commit

Permalink
Make attribute formatting part of StringTemplate
Browse files Browse the repository at this point in the history
Since attributes are basically string templates with a tiny bit of logic it felt
that these methods would belong here. In the long term I hope to remove them from
Helper and have Helper use these methods instead.
  • Loading branch information
markstory committed Jan 8, 2014
1 parent b5b151e commit 5855bda
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 1 deletion.
100 changes: 99 additions & 1 deletion Cake/View/StringTemplate.php
Expand Up @@ -27,12 +27,26 @@
*/
class StringTemplate {

/**
* List of attributes that can be made compact.
*
* @var array
*/
protected $_compactAttributes = array(
'compact', 'checked', 'declare', 'readonly', 'disabled', 'selected',
'defer', 'ismap', 'nohref', 'noshade', 'nowrap', 'multiple', 'noresize',
'autoplay', 'controls', 'loop', 'muted', 'required', 'novalidate', 'formnovalidate'
);

/**
* The templates this instance holds.
*
* @var array
*/
protected $_templates = [];
protected $_templates = [
'attribute' => '{{name}}="{{value}}"',
'compactAttribute' => '{{name}}="{{value}}"',
];

/**
* Load a config file containing templates.
Expand Down Expand Up @@ -111,4 +125,88 @@ public function format($name, array $data) {
return strtr($template, $replace);
}

/**
* Returns a space-delimited string with items of the $options array. If a key
* of $options array happens to be one of those listed
* in `StringTemplate::$_compactAttributes` and its value is one of:
*
* - '1' (string)
* - 1 (integer)
* - true (boolean)
* - 'true' (string)
*
* Then the value will be reset to be identical with key's name.
* If the value is not one of these 4, the parameter is not output.
*
* 'escape' is a special option in that it controls the conversion of
* attributes to their html-entity encoded equivalents. Set to false to disable html-encoding.
*
* If value for any option key is set to `null` or `false`, that option will be excluded from output.
*
* This method uses the 'attribute' and 'compactAttribute' templates. Each of
* these templates uses the `name` and `value` variables. You can modify these
* templates to change how attributes are formatted.
*
* @param array $options Array of options.
* @param array $exclude Array of options to be excluded, the options here will not be part of the return.
* @param string $insertBefore String to be inserted before options.
* @return string Composed attributes.
*/
public function formatAttributes($options, $exclude = null) {
$insertBefore = ' ';
$options = (array)$options + ['escape' => true];

if (!is_array($exclude)) {
$exclude = [];
}

$exclude = ['escape' => true] + array_flip($exclude);
$escape = $options['escape'];
$attributes = [];

foreach ($options as $key => $value) {
if (!isset($exclude[$key]) && $value !== false && $value !== null) {
$attributes[] = $this->_formatAttribute($key, $value, $escape);
}
}
$out = trim(implode(' ', $attributes));
return $out ? $insertBefore . $out : '';
}

/**
* Formats an individual attribute, and returns the string value of the composed attribute.
* Works with minimized attributes that have the same value as their name such as 'disabled' and 'checked'
*
* @param string $key The name of the attribute to create
* @param string $value The value of the attribute to create.
* @param boolean $escape Define if the value must be escaped
* @return string The composed attribute.
*/
protected function _formatAttribute($key, $value, $escape = true) {
if (is_array($value)) {
$value = implode(' ', $value);
}
if (is_numeric($key)) {
return $this->format('compactAttribute', [
'name' => $value,
'value' => $value
]);
}
$truthy = [1, '1', true, 'true', $key];
$isMinimized = in_array($key, $this->_compactAttributes);
if ($isMinimized && in_array($value, $truthy, true)) {
return $this->format('compactAttribute', [
'name' => $key,
'value' => $key
]);
}
if ($isMinimized) {
return '';
}
return $this->format('attribute', [
'name' => $key,
'value' => $escape ? h($value) : $value
]);
}

}
65 changes: 65 additions & 0 deletions Test/TestCase/View/StringTemplateTest.php
Expand Up @@ -86,6 +86,8 @@ public function testFormat() {
* @return void
*/
public function testLoad() {
$this->template->remove('attribute');
$this->template->remove('compactAttribute');
$this->assertEquals([], $this->template->get());
$this->assertNull($this->template->load('test_templates'));
$this->assertEquals('<a href="{{url}}">{{text}}</a>', $this->template->get('link'));
Expand All @@ -112,4 +114,67 @@ public function testLoadErrorNoFile() {
$this->template->load('no_such_file');
}

/**
* Test formatting compact attributes.
*
* @return void
*/
public function testFormatAttributesCompact() {
$attrs = ['disabled' => true, 'selected' => 1, 'checked' => '1', 'multiple' => 'multiple'];
$result = $this->template->formatAttributes($attrs);
$this->assertEquals(
' disabled="disabled" selected="selected" checked="checked" multiple="multiple"',
$result
);

$attrs = ['disabled' => false, 'selected' => 0, 'checked' => '0', 'multiple' => null];
$result = $this->template->formatAttributes($attrs);
$this->assertEquals(
'',
$result
);
}

/**
* Test formatting normal attributes.
*
* @return void
*/
public function testFormatAttributes() {
$attrs = ['name' => 'bruce', 'data-hero' => '<batman>'];
$result = $this->template->formatAttributes($attrs);
$this->assertEquals(
' name="bruce" data-hero="&lt;batman&gt;"',
$result
);

$attrs = ['escape' => false, 'name' => 'bruce', 'data-hero' => '<batman>'];
$result = $this->template->formatAttributes($attrs);
$this->assertEquals(
' name="bruce" data-hero="<batman>"',
$result
);

$attrs = ['name' => 'bruce', 'data-hero' => '<batman>'];
$result = $this->template->formatAttributes($attrs, ['name']);
$this->assertEquals(
' data-hero="&lt;batman&gt;"',
$result
);
}

/**
* Test formatting array attributes.
*
* @return void
*/
public function testFormatAttributesArray() {
$attrs = ['name' => ['bruce', 'wayne']];
$result = $this->template->formatAttributes($attrs);
$this->assertEquals(
' name="bruce wayne"',
$result
);
}

}

0 comments on commit 5855bda

Please sign in to comment.