From b43d68f9b40098f6f8bbda66b3e571636b4235ac Mon Sep 17 00:00:00 2001 From: Maxime Rainville Date: Tue, 27 Jul 2021 17:03:27 +1200 Subject: [PATCH] API Add an AttributesHTML trait --- src/Forms/Form.php | 75 +------------------- src/Forms/FormField.php | 125 +------------------------------- src/View/AttributesHTML.php | 138 ++++++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 194 deletions(-) create mode 100644 src/View/AttributesHTML.php diff --git a/src/Forms/Form.php b/src/Forms/Form.php index 9a6fd11d1..16724e50d 100644 --- a/src/Forms/Form.php +++ b/src/Forms/Form.php @@ -20,6 +20,7 @@ use SilverStripe\ORM\FieldType\DBHTMLText; use SilverStripe\ORM\ValidationResult; use SilverStripe\Security\NullSecurityToken; use SilverStripe\Security\SecurityToken; +use SilverStripe\View\AttributesHTML; use SilverStripe\View\SSViewer; use SilverStripe\View\ViewableData; @@ -66,10 +67,10 @@ use SilverStripe\View\ViewableData; */ class Form extends ViewableData implements HasRequestHandler { + use AttributesHTML; use FormMessage; /** - * Default form Name property */ const DEFAULT_NAME = 'Form'; @@ -816,33 +817,7 @@ class Form extends ViewableData implements HasRequestHandler return $this; } - /** - * @param string $name - * @param string $value - * @return $this - */ - public function setAttribute($name, $value) - { - $this->attributes[$name] = $value; - return $this; - } - - /** - * @param string $name - * @return string - */ - public function getAttribute($name) - { - if (isset($this->attributes[$name])) { - return $this->attributes[$name]; - } - return null; - } - - /** - * @return array - */ - public function getAttributes() + protected function getDefaultAttributes(): array { $attrs = [ 'id' => $this->FormName(), @@ -860,53 +835,9 @@ class Form extends ViewableData implements HasRequestHandler $attrs['class'] .= ' validationerror'; } - $attrs = array_merge($attrs, $this->attributes); - return $attrs; } - /** - * Return the attributes of the form tag - used by the templates. - * - * @param array $attrs Custom attributes to process. Falls back to {@link getAttributes()}. - * If at least one argument is passed as a string, all arguments act as excludes by name. - * - * @return string HTML attributes, ready for insertion into an HTML tag - */ - public function getAttributesHTML($attrs = null) - { - $exclude = (is_string($attrs)) ? func_get_args() : null; - - $attrs = $this->getAttributes(); - - // Remove empty - $attrs = array_filter((array)$attrs, function ($value) { - return ($value || $value === 0); - }); - - // Remove excluded - if ($exclude) { - $attrs = array_diff_key($attrs, array_flip($exclude)); - } - - // Prepare HTML-friendly 'method' attribute (lower-case) - if (isset($attrs['method'])) { - $attrs['method'] = strtolower($attrs['method']); - } - - // Create markup - $parts = []; - foreach ($attrs as $name => $value) { - if ($value === true) { - $value = $name; - } - - $parts[] = sprintf('%s="%s"', Convert::raw2att($name), Convert::raw2att($value)); - } - - return implode(' ', $parts); - } - public function FormAttributes() { return $this->getAttributesHTML(); diff --git a/src/Forms/FormField.php b/src/Forms/FormField.php index 98f25b735..2e2caab9b 100644 --- a/src/Forms/FormField.php +++ b/src/Forms/FormField.php @@ -13,6 +13,7 @@ use SilverStripe\ORM\DataObjectInterface; use SilverStripe\ORM\FieldType\DBField; use SilverStripe\ORM\FieldType\DBHTMLText; use SilverStripe\ORM\ValidationResult; +use SilverStripe\View\AttributesHTML; use SilverStripe\View\SSViewer; /** @@ -41,6 +42,7 @@ use SilverStripe\View\SSViewer; */ class FormField extends RequestHandler { + use AttributesHTML; use FormMessage; /** @see $schemaDataType */ @@ -214,17 +216,6 @@ class FormField extends RequestHandler */ protected $smallFieldHolderTemplate; - /** - * All attributes on the form field (not the field holder). - * - * Partially determined based on other instance properties. - * - * @see getAttributes() - * - * @var array - */ - protected $attributes = []; - /** * The data type backing the field. Represents the type of value the * form expects to receive via a postback. Should be set in subclasses. @@ -659,59 +650,7 @@ class FormField extends RequestHandler return $this; } - /** - * Set an HTML attribute on the field element, mostly an input tag. - * - * Some attributes are best set through more specialized methods, to avoid interfering with - * built-in behaviour: - * - * - 'class': {@link addExtraClass()} - * - 'title': {@link setDescription()} - * - 'value': {@link setValue} - * - 'name': {@link setName} - * - * Caution: this doesn't work on most fields which are composed of more than one HTML form - * field. - * - * @param string $name - * @param string $value - * - * @return $this - */ - public function setAttribute($name, $value) - { - $this->attributes[$name] = $value; - - return $this; - } - - /** - * Get an HTML attribute defined by the field, or added through {@link setAttribute()}. - * - * Caution: this doesn't work on all fields, see {@link setAttribute()}. - * - * @param string $name - * @return string - */ - public function getAttribute($name) - { - $attributes = $this->getAttributes(); - - if (isset($attributes[$name])) { - return $attributes[$name]; - } - - return null; - } - - /** - * Allows customization through an 'updateAttributes' hook on the base class. - * Existing attributes are passed in as the first argument and can be manipulated, - * but any attributes added through a subclass implementation won't be included. - * - * @return array - */ - public function getAttributes() + protected function getDefaultAttributes(): array { $attributes = [ 'type' => $this->getInputType(), @@ -729,67 +668,9 @@ class FormField extends RequestHandler $attributes['aria-required'] = 'true'; } - $attributes = array_merge($attributes, $this->attributes); - - $this->extend('updateAttributes', $attributes); - return $attributes; } - /** - * Custom attributes to process. Falls back to {@link getAttributes()}. - * - * If at least one argument is passed as a string, all arguments act as excludes, by name. - * - * @param array $attributes - * - * @return string - */ - public function getAttributesHTML($attributes = null) - { - $exclude = null; - - if (is_string($attributes)) { - $exclude = func_get_args(); - } - - if (!$attributes || is_string($attributes)) { - $attributes = $this->getAttributes(); - } - - $attributes = (array) $attributes; - - $attributes = array_filter($attributes, function ($v) { - return ($v || $v === 0 || $v === '0'); - }); - - if ($exclude) { - $attributes = array_diff_key( - $attributes, - array_flip($exclude) - ); - } - - // Create markup - $parts = []; - - foreach ($attributes as $name => $value) { - if ($value === true) { - $value = $name; - } else { - if (is_scalar($value)) { - $value = (string) $value; - } else { - $value = json_encode($value); - } - } - - $parts[] = sprintf('%s="%s"', Convert::raw2att($name), Convert::raw2att($value)); - } - - return implode(' ', $parts); - } - /** * Returns a version of a title suitable for insertion into an HTML attribute. * diff --git a/src/View/AttributesHTML.php b/src/View/AttributesHTML.php new file mode 100644 index 000000000..7567ea859 --- /dev/null +++ b/src/View/AttributesHTML.php @@ -0,0 +1,138 @@ + 'HTMLFragment', + * 'getAttributesHTML' => 'HTMLFragment', + * ]; + * ``` + */ +trait AttributesHTML +{ + + /** + * List of attributes to render on the frontend + * @var array + */ + protected $attributes = []; + + /** + * Set an HTML attribute + * @param $name + * @param $value + * @return $this + */ + public function setAttribute($name, $value) + { + $this->attributes[$name] = $value; + + return $this; + } + + /** + * Retrieve the value of an HTML attribute + * @param string $name + * @return mixed|null + */ + public function getAttribute($name) + { + $attributes = $this->getAttributes(); + + if (isset($attributes[$name])) { + return $attributes[$name]; + } + + return null; + } + + /** + * Get the default attributes when rendering this object. + * + * Called by `getAttributes()` + * + * @return array + */ + abstract protected function getDefaultAttributes(): array; + + /** + * Allows customization through an 'updateAttributes' hook on the base class. + * Existing attributes are passed in as the first argument and can be manipulated, + * but any attributes added through a subclass implementation won't be included. + * + * @return array + */ + public function getAttributes() + { + $defaultAttributes = $this->getDefaultAttributes(); + + $attributes = array_merge($defaultAttributes, $this->attributes); + + if (method_exists($this, 'extend')) { + $this->extend('updateAttributes', $attributes); + } + + return $attributes; + } + + /** + * Custom attributes to process. Falls back to {@link getAttributes()}. + * + * If at least one argument is passed as a string, all arguments act as excludes, by name. + * + * @param array $attributes + * + * @return string + */ + public function getAttributesHTML($attributes = null) + { + $exclude = null; + + if (is_string($attributes)) { + $exclude = func_get_args(); + } + + if (!$attributes || is_string($attributes)) { + $attributes = $this->getAttributes(); + } + + $attributes = (array) $attributes; + + $attributes = array_filter($attributes, function ($v) { + return ($v || $v === 0 || $v === '0'); + }); + + if ($exclude) { + $attributes = array_diff_key( + $attributes, + array_flip($exclude) + ); + } + + // Create markup + $parts = []; + + foreach ($attributes as $name => $value) { + if ($value === true) { + $value = $name; + } else { + if (is_scalar($value)) { + $value = (string) $value; + } else { + $value = json_encode($value); + } + } + + $parts[] = sprintf('%s="%s"', Convert::raw2att($name), Convert::raw2att($value)); + } + + return implode(' ', $parts); + } +}