xref: /dokuwiki/inc/Form/DropdownElement.php (revision 4eed441dbdf4a0249f87fc14feb3252071518bb4)
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
13238a072bSMichael Grosse    /** @var array OptGroup[] */
14238a072bSMichael Grosse    protected $optGroups = array();
158638ead5SAndreas Gohr
168638ead5SAndreas Gohr    /**
178638ead5SAndreas Gohr     * @param string $name The name of this form element
18*4eed441dSMichael Grosse     * @param array  $options The available options
198638ead5SAndreas Gohr     * @param string $label The label text for this element (will be autoescaped)
208638ead5SAndreas Gohr     */
218638ead5SAndreas Gohr    public function __construct($name, $options, $label = '') {
228638ead5SAndreas Gohr        parent::__construct('dropdown', $name, $label);
23693978b1SMichael Grosse        $this->rmattr('type');
24*4eed441dSMichael Grosse        $this->optGroups[''] = new OptGroup(null, $options);
25238a072bSMichael Grosse        $this->val('');
26238a072bSMichael Grosse    }
27238a072bSMichael Grosse
28238a072bSMichael Grosse    /**
29*4eed441dSMichael Grosse     * Add an `<optgroup>` and respective options
30*4eed441dSMichael Grosse     *
31*4eed441dSMichael Grosse     * @param string $label
32*4eed441dSMichael Grosse     * @param array  $options
33*4eed441dSMichael Grosse     * @return OptGroup a reference to the added optgroup
34238a072bSMichael Grosse     * @throws \Exception
35238a072bSMichael Grosse     */
36238a072bSMichael Grosse    public function addOptGroup($label, $options) {
37238a072bSMichael Grosse        if (empty($label)) {
38238a072bSMichael Grosse            throw new \InvalidArgumentException(hsc('<optgroup> must have a label!'));
39238a072bSMichael Grosse        }
40*4eed441dSMichael Grosse        $this->optGroups[$label] = new OptGroup($label, $options);
41238a072bSMichael Grosse        return end($this->optGroups);
42238a072bSMichael Grosse    }
43238a072bSMichael Grosse
44238a072bSMichael Grosse    /**
45238a072bSMichael Grosse     * Set or get the optgroups of an Dropdown-Element.
46238a072bSMichael Grosse     *
47238a072bSMichael Grosse     * optgroups have to be given as associative array
48238a072bSMichael Grosse     *   * the key being the label of the group
49238a072bSMichael Grosse     *   * the value being an array of options as defined in @see OptGroup::options()
50238a072bSMichael Grosse     *
51238a072bSMichael Grosse     * @param null|array $optGroups
522bd1d2c7SMichael Grosse     * @return OptGroup[]|DropdownElement
53238a072bSMichael Grosse     */
54238a072bSMichael Grosse    public function optGroups($optGroups = null) {
55238a072bSMichael Grosse        if($optGroups === null) {
56238a072bSMichael Grosse            return $this->optGroups;
57238a072bSMichael Grosse        }
58238a072bSMichael Grosse        if (!is_array($optGroups)) {
59238a072bSMichael Grosse            throw new \InvalidArgumentException(hsc('Argument must be an associative array of label => [options]!'));
60238a072bSMichael Grosse        }
61238a072bSMichael Grosse        $this->optGroups = array();
62238a072bSMichael Grosse        foreach ($optGroups as $label => $options) {
63238a072bSMichael Grosse            $this->addOptGroup($label, $options);
64238a072bSMichael Grosse        }
652bd1d2c7SMichael Grosse        return $this;
668638ead5SAndreas Gohr    }
678638ead5SAndreas Gohr
688638ead5SAndreas Gohr    /**
698638ead5SAndreas Gohr     * Get or set the options of the Dropdown
708638ead5SAndreas Gohr     *
718638ead5SAndreas Gohr     * Options can be given as associative array (value => label) or as an
72795955b2SMichael Grosse     * indexd array (label = value) or as an array of arrays. In the latter
73795955b2SMichael Grosse     * case an element has to look as follows:
74795955b2SMichael Grosse     * option-value => array (
75795955b2SMichael Grosse     *                 'label' => option-label,
76795955b2SMichael Grosse     *                 'attrs' => array (
77795955b2SMichael Grosse     *                                    attr-key => attr-value, ...
78795955b2SMichael Grosse     *                                  )
79795955b2SMichael Grosse     *                 )
808638ead5SAndreas Gohr     *
818638ead5SAndreas Gohr     * @param null|array $options
828638ead5SAndreas Gohr     * @return $this|array
838638ead5SAndreas Gohr     */
848638ead5SAndreas Gohr    public function options($options = null) {
85238a072bSMichael Grosse        if ($options === null) {
86*4eed441dSMichael Grosse            return $this->optGroups['']->options();
878638ead5SAndreas Gohr        }
88*4eed441dSMichael Grosse        $this->optGroups[''] = new OptGroup(null, $options);
898638ead5SAndreas Gohr        return $this;
908638ead5SAndreas Gohr    }
918638ead5SAndreas Gohr
928638ead5SAndreas Gohr    /**
938638ead5SAndreas Gohr     * Gets or sets an attribute
948638ead5SAndreas Gohr     *
958638ead5SAndreas Gohr     * When no $value is given, the current content of the attribute is returned.
968638ead5SAndreas Gohr     * An empty string is returned for unset attributes.
978638ead5SAndreas Gohr     *
988638ead5SAndreas Gohr     * When a $value is given, the content is set to that value and the Element
998638ead5SAndreas Gohr     * itself is returned for easy chaining
1008638ead5SAndreas Gohr     *
1018638ead5SAndreas Gohr     * @param string $name Name of the attribute to access
1028638ead5SAndreas Gohr     * @param null|string $value New value to set
1038638ead5SAndreas Gohr     * @return string|$this
1048638ead5SAndreas Gohr     */
1058638ead5SAndreas Gohr    public function attr($name, $value = null) {
1068638ead5SAndreas Gohr        if(strtolower($name) == 'multiple') {
1078638ead5SAndreas Gohr            throw new \InvalidArgumentException('Sorry, the dropdown element does not support the "multiple" attribute');
1088638ead5SAndreas Gohr        }
1098638ead5SAndreas Gohr        return parent::attr($name, $value);
1108638ead5SAndreas Gohr    }
1118638ead5SAndreas Gohr
1128638ead5SAndreas Gohr    /**
1138638ead5SAndreas Gohr     * Get or set the current value
1148638ead5SAndreas Gohr     *
1158638ead5SAndreas Gohr     * When setting a value that is not defined in the options, the value is ignored
1168638ead5SAndreas Gohr     * and the first option's value is selected instead
1178638ead5SAndreas Gohr     *
1188638ead5SAndreas Gohr     * @param null|string $value The value to set
1198638ead5SAndreas Gohr     * @return $this|string
1208638ead5SAndreas Gohr     */
1218638ead5SAndreas Gohr    public function val($value = null) {
1228638ead5SAndreas Gohr        if($value === null) return $this->value;
1238638ead5SAndreas Gohr
124238a072bSMichael Grosse        $value_exists = $this->setValueInOptGroups($value);
125238a072bSMichael Grosse
126238a072bSMichael Grosse        if($value_exists) {
1278638ead5SAndreas Gohr            $this->value = $value;
1288638ead5SAndreas Gohr        } else {
1298638ead5SAndreas Gohr            // unknown value set, select first option instead
130238a072bSMichael Grosse            $this->value = $this->getFirstOption();
131238a072bSMichael Grosse            $this->setValueInOptGroups($this->value);
1328638ead5SAndreas Gohr        }
1338638ead5SAndreas Gohr
1348638ead5SAndreas Gohr        return $this;
1358638ead5SAndreas Gohr    }
1368638ead5SAndreas Gohr
1378638ead5SAndreas Gohr    /**
138238a072bSMichael Grosse     * Returns the first options as it will be rendered in HTML
139238a072bSMichael Grosse     *
140238a072bSMichael Grosse     * @return string
141238a072bSMichael Grosse     */
142238a072bSMichael Grosse    protected function getFirstOption() {
143238a072bSMichael Grosse        $options = $this->options();
144238a072bSMichael Grosse        if (!empty($options)) {
145238a072bSMichael Grosse            return (string) array_shift(array_keys($options));
146238a072bSMichael Grosse        }
147238a072bSMichael Grosse        foreach ($this->optGroups as $optGroup) {
148238a072bSMichael Grosse            $options = $optGroup->options();
149238a072bSMichael Grosse            if (!empty($options)) {
150238a072bSMichael Grosse                return (string) array_shift(array_keys($options));
151238a072bSMichael Grosse            }
152238a072bSMichael Grosse        }
153238a072bSMichael Grosse    }
154238a072bSMichael Grosse
155238a072bSMichael Grosse    /**
156238a072bSMichael Grosse     * Set the value in the OptGroups, including the optgroup for the options without optgroup.
157238a072bSMichael Grosse     *
158238a072bSMichael Grosse     * @param string $value
159238a072bSMichael Grosse     * @return bool
160238a072bSMichael Grosse     */
161238a072bSMichael Grosse    protected function setValueInOptGroups($value) {
162068abb2bSMichael Grosse        $value_exists = false;
163068abb2bSMichael Grosse        /** @var OptGroup $optGroup */
164*4eed441dSMichael Grosse        foreach ($this->optGroups as $optGroup) {
1659c3fca6dSMichael Grosse            $value_exists = $optGroup->storeValue($value) || $value_exists;
16645082b9eSMichael Grosse            if ($value_exists) {
167068abb2bSMichael Grosse                $value = null;
168068abb2bSMichael Grosse            }
169238a072bSMichael Grosse        }
170238a072bSMichael Grosse        return $value_exists;
171238a072bSMichael Grosse    }
172238a072bSMichael Grosse
173238a072bSMichael Grosse    /**
1748638ead5SAndreas Gohr     * Create the HTML for the select it self
1758638ead5SAndreas Gohr     *
1768638ead5SAndreas Gohr     * @return string
1778638ead5SAndreas Gohr     */
1788638ead5SAndreas Gohr    protected function mainElementHTML() {
1798638ead5SAndreas Gohr        if($this->useInput) $this->prefillInput();
1808638ead5SAndreas Gohr
1818638ead5SAndreas Gohr        $html = '<select ' . buildAttributes($this->attrs()) . '>';
182238a072bSMichael Grosse        $html = array_reduce($this->optGroups, function($html, OptGroup $optGroup) {return $html . $optGroup->toHTML();}, $html);
1838638ead5SAndreas Gohr        $html .= '</select>';
1848638ead5SAndreas Gohr
1858638ead5SAndreas Gohr        return $html;
1868638ead5SAndreas Gohr    }
1878638ead5SAndreas Gohr
1888638ead5SAndreas Gohr}
189