xref: /dokuwiki/inc/Form/DropdownElement.php (revision 238a072b2d4d4c1123065f1f5bb865869e7edd58)
18638ead5SAndreas Gohr<?php
28638ead5SAndreas Gohrnamespace dokuwiki\Form;
38638ead5SAndreas Gohr
48638ead5SAndreas Gohr/**
58638ead5SAndreas Gohr * Class DropdownElement
68638ead5SAndreas Gohr *
78638ead5SAndreas Gohr * Represents a HTML select. Please note that this does not support multiple selected options!
88638ead5SAndreas Gohr *
98638ead5SAndreas Gohr * @package dokuwiki\Form
108638ead5SAndreas Gohr */
118638ead5SAndreas Gohrclass DropdownElement extends InputElement {
128638ead5SAndreas Gohr
13*238a072bSMichael Grosse    /** @var OptGroup */
14*238a072bSMichael Grosse    protected $options = null;
15*238a072bSMichael Grosse
16*238a072bSMichael Grosse    /** @var array OptGroup[] */
17*238a072bSMichael Grosse    protected $optGroups = array();
188638ead5SAndreas Gohr
198638ead5SAndreas Gohr    protected $value = '';
208638ead5SAndreas Gohr
218638ead5SAndreas Gohr    /**
228638ead5SAndreas Gohr     * @param string $name The name of this form element
238638ead5SAndreas Gohr     * @param string $options The available options
248638ead5SAndreas Gohr     * @param string $label The label text for this element (will be autoescaped)
258638ead5SAndreas Gohr     */
268638ead5SAndreas Gohr    public function __construct($name, $options, $label = '') {
278638ead5SAndreas Gohr        parent::__construct('dropdown', $name, $label);
28693978b1SMichael Grosse        $this->rmattr('type');
29*238a072bSMichael Grosse        $this->options = new OptGroup(null, $options);
30*238a072bSMichael Grosse        $this->val('');
31*238a072bSMichael Grosse    }
32*238a072bSMichael Grosse
33*238a072bSMichael Grosse    /**
34*238a072bSMichael Grosse     * @param $label
35*238a072bSMichael Grosse     * @param $options
36*238a072bSMichael Grosse     * @return mixed
37*238a072bSMichael Grosse     * @throws \Exception
38*238a072bSMichael Grosse     */
39*238a072bSMichael Grosse    public function addOptGroup($label, $options) {
40*238a072bSMichael Grosse        if (empty($label)) {
41*238a072bSMichael Grosse            throw new \InvalidArgumentException(hsc('<optgroup> must have a label!'));
42*238a072bSMichael Grosse        }
43*238a072bSMichael Grosse        $this->optGroups[] = new OptGroup($label, $options);
44*238a072bSMichael Grosse        return end($this->optGroups);
45*238a072bSMichael Grosse    }
46*238a072bSMichael Grosse
47*238a072bSMichael Grosse    /**
48*238a072bSMichael Grosse     * Set or get the optgroups of an Dropdown-Element.
49*238a072bSMichael Grosse     *
50*238a072bSMichael Grosse     * optgroups have to be given as associative array
51*238a072bSMichael Grosse     *   * the key being the label of the group
52*238a072bSMichael Grosse     *   * the value being an array of options as defined in @see OptGroup::options()
53*238a072bSMichael Grosse     *
54*238a072bSMichael Grosse     * @param null|array $optGroups
55*238a072bSMichael Grosse     * @return array
56*238a072bSMichael Grosse     */
57*238a072bSMichael Grosse    public function optGroups($optGroups = null) {
58*238a072bSMichael Grosse        if($optGroups === null) {
59*238a072bSMichael Grosse            return $this->optGroups;
60*238a072bSMichael Grosse        }
61*238a072bSMichael Grosse        if (!is_array($optGroups)) {
62*238a072bSMichael Grosse            throw new \InvalidArgumentException(hsc('Argument must be an associative array of label => [options]!'));
63*238a072bSMichael Grosse        }
64*238a072bSMichael Grosse        $this->optGroups = array();
65*238a072bSMichael Grosse        foreach ($optGroups as $label => $options) {
66*238a072bSMichael Grosse            $this->addOptGroup($label, $options);
67*238a072bSMichael Grosse        }
688638ead5SAndreas Gohr    }
698638ead5SAndreas Gohr
708638ead5SAndreas Gohr    /**
718638ead5SAndreas Gohr     * Get or set the options of the Dropdown
728638ead5SAndreas Gohr     *
738638ead5SAndreas Gohr     * Options can be given as associative array (value => label) or as an
74795955b2SMichael Grosse     * indexd array (label = value) or as an array of arrays. In the latter
75795955b2SMichael Grosse     * case an element has to look as follows:
76795955b2SMichael Grosse     * option-value => array (
77795955b2SMichael Grosse     *                 'label' => option-label,
78795955b2SMichael Grosse     *                 'attrs' => array (
79795955b2SMichael Grosse     *                                    attr-key => attr-value, ...
80795955b2SMichael Grosse     *                                  )
81795955b2SMichael Grosse     *                 )
828638ead5SAndreas Gohr     *
838638ead5SAndreas Gohr     * @param null|array $options
848638ead5SAndreas Gohr     * @return $this|array
858638ead5SAndreas Gohr     */
868638ead5SAndreas Gohr    public function options($options = null) {
87*238a072bSMichael Grosse        if ($options === null) {
88*238a072bSMichael Grosse            return $this->options->options();
898638ead5SAndreas Gohr        }
90*238a072bSMichael Grosse        $this->options = new OptGroup(null, $options);
918638ead5SAndreas Gohr        return $this;
928638ead5SAndreas Gohr    }
938638ead5SAndreas Gohr
948638ead5SAndreas Gohr    /**
958638ead5SAndreas Gohr     * Gets or sets an attribute
968638ead5SAndreas Gohr     *
978638ead5SAndreas Gohr     * When no $value is given, the current content of the attribute is returned.
988638ead5SAndreas Gohr     * An empty string is returned for unset attributes.
998638ead5SAndreas Gohr     *
1008638ead5SAndreas Gohr     * When a $value is given, the content is set to that value and the Element
1018638ead5SAndreas Gohr     * itself is returned for easy chaining
1028638ead5SAndreas Gohr     *
1038638ead5SAndreas Gohr     * @param string $name Name of the attribute to access
1048638ead5SAndreas Gohr     * @param null|string $value New value to set
1058638ead5SAndreas Gohr     * @return string|$this
1068638ead5SAndreas Gohr     */
1078638ead5SAndreas Gohr    public function attr($name, $value = null) {
1088638ead5SAndreas Gohr        if(strtolower($name) == 'multiple') {
1098638ead5SAndreas Gohr            throw new \InvalidArgumentException('Sorry, the dropdown element does not support the "multiple" attribute');
1108638ead5SAndreas Gohr        }
1118638ead5SAndreas Gohr        return parent::attr($name, $value);
1128638ead5SAndreas Gohr    }
1138638ead5SAndreas Gohr
1148638ead5SAndreas Gohr    /**
1158638ead5SAndreas Gohr     * Get or set the current value
1168638ead5SAndreas Gohr     *
1178638ead5SAndreas Gohr     * When setting a value that is not defined in the options, the value is ignored
1188638ead5SAndreas Gohr     * and the first option's value is selected instead
1198638ead5SAndreas Gohr     *
1208638ead5SAndreas Gohr     * @param null|string $value The value to set
1218638ead5SAndreas Gohr     * @return $this|string
1228638ead5SAndreas Gohr     */
1238638ead5SAndreas Gohr    public function val($value = null) {
1248638ead5SAndreas Gohr        if($value === null) return $this->value;
1258638ead5SAndreas Gohr
126*238a072bSMichael Grosse        $value_exists = $this->setValueInOptGroups($value);
127*238a072bSMichael Grosse
128*238a072bSMichael Grosse        if($value_exists) {
1298638ead5SAndreas Gohr            $this->value = $value;
1308638ead5SAndreas Gohr        } else {
1318638ead5SAndreas Gohr            // unknown value set, select first option instead
132*238a072bSMichael Grosse            $this->value = $this->getFirstOption();
133*238a072bSMichael Grosse            $this->setValueInOptGroups($this->value);
1348638ead5SAndreas Gohr        }
1358638ead5SAndreas Gohr
1368638ead5SAndreas Gohr        return $this;
1378638ead5SAndreas Gohr    }
1388638ead5SAndreas Gohr
1398638ead5SAndreas Gohr    /**
140*238a072bSMichael Grosse     * Returns the first options as it will be rendered in HTML
141*238a072bSMichael Grosse     *
142*238a072bSMichael Grosse     * @return string
143*238a072bSMichael Grosse     */
144*238a072bSMichael Grosse    protected function getFirstOption() {
145*238a072bSMichael Grosse        $options = $this->options();
146*238a072bSMichael Grosse        if (!empty($options)) {
147*238a072bSMichael Grosse            return (string) array_shift(array_keys($options));
148*238a072bSMichael Grosse        }
149*238a072bSMichael Grosse        foreach ($this->optGroups as $optGroup) {
150*238a072bSMichael Grosse            $options = $optGroup->options();
151*238a072bSMichael Grosse            if (!empty($options)) {
152*238a072bSMichael Grosse                return (string) array_shift(array_keys($options));
153*238a072bSMichael Grosse            }
154*238a072bSMichael Grosse        }
155*238a072bSMichael Grosse    }
156*238a072bSMichael Grosse
157*238a072bSMichael Grosse    /**
158*238a072bSMichael Grosse     * Set the value in the OptGroups, including the optgroup for the options without optgroup.
159*238a072bSMichael Grosse     *
160*238a072bSMichael Grosse     * @param string $value
161*238a072bSMichael Grosse     * @return bool
162*238a072bSMichael Grosse     */
163*238a072bSMichael Grosse    protected function setValueInOptGroups($value) {
164*238a072bSMichael Grosse        $value_exists = $this->options->setValue($value);
165*238a072bSMichael Grosse        foreach ($this->optGroups as $optGroup) {
166*238a072bSMichael Grosse            $value_exists = $optGroup->setValue($value) || $value_exists;
167*238a072bSMichael Grosse        }
168*238a072bSMichael Grosse        return $value_exists;
169*238a072bSMichael Grosse    }
170*238a072bSMichael Grosse
171*238a072bSMichael Grosse    /**
1728638ead5SAndreas Gohr     * Create the HTML for the select it self
1738638ead5SAndreas Gohr     *
1748638ead5SAndreas Gohr     * @return string
1758638ead5SAndreas Gohr     */
1768638ead5SAndreas Gohr    protected function mainElementHTML() {
1778638ead5SAndreas Gohr        if($this->useInput) $this->prefillInput();
1788638ead5SAndreas Gohr
1798638ead5SAndreas Gohr        $html = '<select ' . buildAttributes($this->attrs()) . '>';
180*238a072bSMichael Grosse        $html .= $this->options->toHTML();
181*238a072bSMichael Grosse        $html = array_reduce($this->optGroups, function($html, OptGroup $optGroup) {return $html . $optGroup->toHTML();}, $html);
1828638ead5SAndreas Gohr        $html .= '</select>';
1838638ead5SAndreas Gohr
1848638ead5SAndreas Gohr        return $html;
1858638ead5SAndreas Gohr    }
1868638ead5SAndreas Gohr
1878638ead5SAndreas Gohr}
188