DropdownElement.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. <?php
  2. namespace dokuwiki\Form;
  3. /**
  4. * Class DropdownElement
  5. *
  6. * Represents a HTML select. Please not that prefilling with input data only works for single values.
  7. *
  8. * @package dokuwiki\Form
  9. */
  10. class DropdownElement extends InputElement
  11. {
  12. /** @var array OptGroup[] */
  13. protected $optGroups = [];
  14. /** @var string[] the currently set values */
  15. protected $values = [];
  16. /**
  17. * @param string $name The name of this form element
  18. * @param array $options The available options
  19. * @param string $label The label text for this element (will be autoescaped)
  20. */
  21. public function __construct($name, $options, $label = '')
  22. {
  23. parent::__construct('dropdown', $name, $label);
  24. $this->rmattr('type');
  25. $this->optGroups[''] = new OptGroup(null, $options);
  26. $this->val('');
  27. }
  28. /**
  29. * Add an `<optgroup>` and respective options
  30. *
  31. * @param string $label
  32. * @param array $options
  33. * @return OptGroup a reference to the added optgroup
  34. * @throws \InvalidArgumentException
  35. */
  36. public function addOptGroup($label, $options)
  37. {
  38. if (empty($label)) {
  39. throw new \InvalidArgumentException(hsc('<optgroup> must have a label!'));
  40. }
  41. $this->optGroups[$label] = new OptGroup($label, $options);
  42. return end($this->optGroups);
  43. }
  44. /**
  45. * Set or get the optgroups of an Dropdown-Element.
  46. *
  47. * optgroups have to be given as associative array
  48. * * the key being the label of the group
  49. * * the value being an array of options as defined in @param null|array $optGroups
  50. * @return OptGroup[]|DropdownElement
  51. * @see OptGroup::options()
  52. *
  53. */
  54. public function optGroups($optGroups = null)
  55. {
  56. if ($optGroups === null) {
  57. return $this->optGroups;
  58. }
  59. if (!is_array($optGroups)) {
  60. throw new \InvalidArgumentException(hsc('Argument must be an associative array of label => [options]!'));
  61. }
  62. $this->optGroups = array();
  63. foreach ($optGroups as $label => $options) {
  64. $this->addOptGroup($label, $options);
  65. }
  66. return $this;
  67. }
  68. /**
  69. * Get or set the options of the Dropdown
  70. *
  71. * Options can be given as associative array (value => label) or as an
  72. * indexd array (label = value) or as an array of arrays. In the latter
  73. * case an element has to look as follows:
  74. * option-value => array (
  75. * 'label' => option-label,
  76. * 'attrs' => array (
  77. * attr-key => attr-value, ...
  78. * )
  79. * )
  80. *
  81. * @param null|array $options
  82. * @return $this|array
  83. */
  84. public function options($options = null)
  85. {
  86. if ($options === null) {
  87. return $this->optGroups['']->options();
  88. }
  89. $this->optGroups[''] = new OptGroup(null, $options);
  90. return $this;
  91. }
  92. /**
  93. * Get or set the current value
  94. *
  95. * When setting a value that is not defined in the options, the value is ignored
  96. * and the first option's value is selected instead
  97. *
  98. * @param null|string|string[] $value The value to set
  99. * @return $this|string|string[]
  100. */
  101. public function val($value = null)
  102. {
  103. // getter
  104. if ($value === null) {
  105. if (isset($this->attributes['multiple'])) {
  106. return $this->values;
  107. } else {
  108. return $this->values[0];
  109. }
  110. }
  111. // setter
  112. $this->values = $this->setValuesInOptGroups((array) $value);
  113. if(!$this->values) {
  114. // unknown value set, select first option instead
  115. $this->values = $this->setValuesInOptGroups((array) $this->getFirstOptionKey());
  116. }
  117. return $this;
  118. }
  119. /**
  120. * Returns the first option's key
  121. *
  122. * @return string
  123. */
  124. protected function getFirstOptionKey()
  125. {
  126. $options = $this->options();
  127. if (!empty($options)) {
  128. $keys = array_keys($options);
  129. return (string)array_shift($keys);
  130. }
  131. foreach ($this->optGroups as $optGroup) {
  132. $options = $optGroup->options();
  133. if (!empty($options)) {
  134. $keys = array_keys($options);
  135. return (string)array_shift($keys);
  136. }
  137. }
  138. return ''; // should not happen
  139. }
  140. /**
  141. * Set the value in the OptGroups, including the optgroup for the options without optgroup.
  142. *
  143. * @param string[] $values The values to be set
  144. * @return string[] The values actually set
  145. */
  146. protected function setValuesInOptGroups($values)
  147. {
  148. $valueset = [];
  149. /** @var OptGroup $optGroup */
  150. foreach ($this->optGroups as $optGroup) {
  151. $found = $optGroup->storeValues($values);
  152. $values = array_diff($values, $found);
  153. $valueset = array_merge($valueset, $found);
  154. }
  155. return $valueset;
  156. }
  157. /**
  158. * Create the HTML for the select it self
  159. *
  160. * @return string
  161. */
  162. protected function mainElementHTML()
  163. {
  164. $attr = $this->attrs();
  165. if (isset($attr['multiple'])) {
  166. // use array notation when multiple values are allowed
  167. $attr['name'] .= '[]';
  168. } elseif ($this->useInput) {
  169. // prefilling is only supported for non-multi fields
  170. $this->prefillInput();
  171. }
  172. $html = '<select ' . buildAttributes($attr) . '>';
  173. $html = array_reduce(
  174. $this->optGroups,
  175. function ($html, OptGroup $optGroup) {
  176. return $html . $optGroup->toHTML();
  177. },
  178. $html
  179. );
  180. $html .= '</select>';
  181. return $html;
  182. }
  183. }