/src/View/Widget/RadioWidget.php

https://github.com/LubosRemplik/cakephp · PHP · 255 lines · 136 code · 24 blank · 95 comment · 23 complexity · a072f89e2295b52c57bc6850dc43b937 MD5 · raw file

  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\View\Widget;
  16. use Cake\View\Form\ContextInterface;
  17. use Cake\View\Helper\IdGeneratorTrait;
  18. use Traversable;
  19. /**
  20. * Input widget class for generating a set of radio buttons.
  21. *
  22. * This class is intended as an internal implementation detail
  23. * of Cake\View\Helper\FormHelper and is not intended for direct use.
  24. */
  25. class RadioWidget implements WidgetInterface
  26. {
  27. use IdGeneratorTrait;
  28. /**
  29. * Template instance.
  30. *
  31. * @var \Cake\View\StringTemplate
  32. */
  33. protected $_templates;
  34. /**
  35. * Label instance.
  36. *
  37. * @var \Cake\View\Widget\LabelWidget
  38. */
  39. protected $_label;
  40. /**
  41. * Constructor
  42. *
  43. * This class uses a few templates:
  44. *
  45. * - `radio` Used to generate the input for a radio button.
  46. * Can use the following variables `name`, `value`, `attrs`.
  47. * - `radioWrapper` Used to generate the container element for
  48. * the radio + input element. Can use the `input` and `label`
  49. * variables.
  50. *
  51. * @param \Cake\View\StringTemplate $templates Templates list.
  52. * @param \Cake\View\Widget\LabelWidget $label Label widget instance.
  53. */
  54. public function __construct($templates, $label)
  55. {
  56. $this->_templates = $templates;
  57. $this->_label = $label;
  58. }
  59. /**
  60. * Render a set of radio buttons.
  61. *
  62. * Data supports the following keys:
  63. *
  64. * - `name` - Set the input name.
  65. * - `options` - An array of options. See below for more information.
  66. * - `disabled` - Either true or an array of inputs to disable.
  67. * When true, the select element will be disabled.
  68. * - `val` - A string of the option to mark as selected.
  69. * - `label` - Either false to disable label generation, or
  70. * an array of attributes for all labels.
  71. * - `required` - Set to true to add the required attribute
  72. * on all generated radios.
  73. * - `idPrefix` Prefix for generated ID attributes.
  74. *
  75. * @param array $data The data to build radio buttons with.
  76. * @param \Cake\View\Form\ContextInterface $context The current form context.
  77. * @return string
  78. */
  79. public function render(array $data, ContextInterface $context)
  80. {
  81. $data += [
  82. 'name' => '',
  83. 'options' => [],
  84. 'disabled' => null,
  85. 'val' => null,
  86. 'escape' => true,
  87. 'label' => true,
  88. 'empty' => false,
  89. 'idPrefix' => null,
  90. 'templateVars' => [],
  91. ];
  92. if ($data['options'] instanceof Traversable) {
  93. $options = iterator_to_array($data['options']);
  94. } else {
  95. $options = (array)$data['options'];
  96. }
  97. if (!empty($data['empty'])) {
  98. $empty = $data['empty'] === true ? 'empty' : $data['empty'];
  99. $options = ['' => $empty] + $options;
  100. }
  101. unset($data['empty']);
  102. $this->_idPrefix = $data['idPrefix'];
  103. $this->_clearIds();
  104. $opts = [];
  105. foreach ($options as $val => $text) {
  106. $opts[] = $this->_renderInput($val, $text, $data, $context);
  107. }
  108. return implode('', $opts);
  109. }
  110. /**
  111. * Disabled attribute detection.
  112. *
  113. * @param array $radio Radio info.
  114. * @param array|null|true $disabled The disabled values.
  115. * @return bool
  116. */
  117. protected function _isDisabled($radio, $disabled)
  118. {
  119. if (!$disabled) {
  120. return false;
  121. }
  122. if ($disabled === true) {
  123. return true;
  124. }
  125. $isNumeric = is_numeric($radio['value']);
  126. return (!is_array($disabled) || in_array((string)$radio['value'], $disabled, !$isNumeric));
  127. }
  128. /**
  129. * Renders a single radio input and label.
  130. *
  131. * @param string|int $val The value of the radio input.
  132. * @param string|array $text The label text, or complex radio type.
  133. * @param array $data Additional options for input generation.
  134. * @param \Cake\View\Form\ContextInterface $context The form context
  135. * @return string
  136. */
  137. protected function _renderInput($val, $text, $data, $context)
  138. {
  139. $escape = $data['escape'];
  140. if (is_int($val) && isset($text['text'], $text['value'])) {
  141. $radio = $text;
  142. } else {
  143. $radio = ['value' => $val, 'text' => $text];
  144. }
  145. $radio['name'] = $data['name'];
  146. if (!isset($radio['templateVars'])) {
  147. $radio['templateVars'] = [];
  148. }
  149. if (!empty($data['templateVars'])) {
  150. $radio['templateVars'] = array_merge($data['templateVars'], $radio['templateVars']);
  151. }
  152. if (empty($radio['id'])) {
  153. $radio['id'] = $this->_id($radio['name'], $radio['value']);
  154. }
  155. if (isset($data['val']) && is_bool($data['val'])) {
  156. $data['val'] = $data['val'] ? 1 : 0;
  157. }
  158. if (isset($data['val']) && (string)$data['val'] === (string)$radio['value']) {
  159. $radio['checked'] = true;
  160. $radio['templateVars']['activeClass'] = 'active';
  161. }
  162. if (!is_bool($data['label']) && isset($radio['checked']) && $radio['checked']) {
  163. $data['label'] = $this->_templates->addClass($data['label'], 'selected');
  164. }
  165. $radio['disabled'] = $this->_isDisabled($radio, $data['disabled']);
  166. if (!empty($data['required'])) {
  167. $radio['required'] = true;
  168. }
  169. if (!empty($data['form'])) {
  170. $radio['form'] = $data['form'];
  171. }
  172. $input = $this->_templates->format('radio', [
  173. 'name' => $radio['name'],
  174. 'value' => $escape ? h($radio['value']) : $radio['value'],
  175. 'templateVars' => $radio['templateVars'],
  176. 'attrs' => $this->_templates->formatAttributes($radio + $data, ['name', 'value', 'text', 'options', 'label', 'val', 'type']),
  177. ]);
  178. $label = $this->_renderLabel(
  179. $radio,
  180. $data['label'],
  181. $input,
  182. $context,
  183. $escape
  184. );
  185. if ($label === false &&
  186. strpos($this->_templates->get('radioWrapper'), '{{input}}') === false
  187. ) {
  188. $label = $input;
  189. }
  190. return $this->_templates->format('radioWrapper', [
  191. 'input' => $input,
  192. 'label' => $label,
  193. 'templateVars' => $data['templateVars'],
  194. ]);
  195. }
  196. /**
  197. * Renders a label element for a given radio button.
  198. *
  199. * In the future this might be refactored into a separate widget as other
  200. * input types (multi-checkboxes) will also need labels generated.
  201. *
  202. * @param array $radio The input properties.
  203. * @param false|string|array $label The properties for a label.
  204. * @param string $input The input widget.
  205. * @param \Cake\View\Form\ContextInterface $context The form context.
  206. * @param bool $escape Whether or not to HTML escape the label.
  207. * @return string|bool Generated label.
  208. */
  209. protected function _renderLabel($radio, $label, $input, $context, $escape)
  210. {
  211. if ($label === false) {
  212. return false;
  213. }
  214. $labelAttrs = is_array($label) ? $label : [];
  215. $labelAttrs += [
  216. 'for' => $radio['id'],
  217. 'escape' => $escape,
  218. 'text' => $radio['text'],
  219. 'templateVars' => $radio['templateVars'],
  220. 'input' => $input,
  221. ];
  222. return $this->_label->render($labelAttrs, $context);
  223. }
  224. /**
  225. * {@inheritDoc}
  226. */
  227. public function secureFields(array $data)
  228. {
  229. return [$data['name']];
  230. }
  231. }