xref: /dokuwiki/inc/Form/DropdownElement.php (revision 64159a61e94d0ce680071c8890e144982c3a8cbe)
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
184eed441dSMichael 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');
244eed441dSMichael Grosse        $this->optGroups[''] = new OptGroup(null, $options);
25238a072bSMichael Grosse        $this->val('');
26238a072bSMichael Grosse    }
27238a072bSMichael Grosse
28238a072bSMichael Grosse    /**
294eed441dSMichael Grosse     * Add an `<optgroup>` and respective options
304eed441dSMichael Grosse     *
314eed441dSMichael Grosse     * @param string $label
324eed441dSMichael Grosse     * @param array  $options
334eed441dSMichael 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        }
404eed441dSMichael 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) {
864eed441dSMichael Grosse            return $this->optGroups['']->options();
878638ead5SAndreas Gohr        }
884eed441dSMichael 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') {
107*64159a61SAndreas Gohr            throw new \InvalidArgumentException(
108*64159a61SAndreas Gohr                'Sorry, the dropdown element does not support the "multiple" attribute'
109*64159a61SAndreas Gohr            );
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
126238a072bSMichael Grosse        $value_exists = $this->setValueInOptGroups($value);
127238a072bSMichael Grosse
128238a072bSMichael Grosse        if($value_exists) {
1298638ead5SAndreas Gohr            $this->value = $value;
1308638ead5SAndreas Gohr        } else {
1318638ead5SAndreas Gohr            // unknown value set, select first option instead
132238a072bSMichael Grosse            $this->value = $this->getFirstOption();
133238a072bSMichael Grosse            $this->setValueInOptGroups($this->value);
1348638ead5SAndreas Gohr        }
1358638ead5SAndreas Gohr
1368638ead5SAndreas Gohr        return $this;
1378638ead5SAndreas Gohr    }
1388638ead5SAndreas Gohr
1398638ead5SAndreas Gohr    /**
140238a072bSMichael Grosse     * Returns the first options as it will be rendered in HTML
141238a072bSMichael Grosse     *
142238a072bSMichael Grosse     * @return string
143238a072bSMichael Grosse     */
144238a072bSMichael Grosse    protected function getFirstOption() {
145238a072bSMichael Grosse        $options = $this->options();
146238a072bSMichael Grosse        if (!empty($options)) {
1477b6bf7a3SMichael Grosse            $keys = array_keys($options);
1487b6bf7a3SMichael Grosse            return (string) array_shift($keys);
149238a072bSMichael Grosse        }
150238a072bSMichael Grosse        foreach ($this->optGroups as $optGroup) {
151238a072bSMichael Grosse            $options = $optGroup->options();
152238a072bSMichael Grosse            if (!empty($options)) {
1537b6bf7a3SMichael Grosse                $keys = array_keys($options);
1547b6bf7a3SMichael Grosse                return (string) array_shift($keys);
155238a072bSMichael Grosse            }
156238a072bSMichael Grosse        }
157238a072bSMichael Grosse    }
158238a072bSMichael Grosse
159238a072bSMichael Grosse    /**
160238a072bSMichael Grosse     * Set the value in the OptGroups, including the optgroup for the options without optgroup.
161238a072bSMichael Grosse     *
162238a072bSMichael Grosse     * @param string $value
163238a072bSMichael Grosse     * @return bool
164238a072bSMichael Grosse     */
165238a072bSMichael Grosse    protected function setValueInOptGroups($value) {
166068abb2bSMichael Grosse        $value_exists = false;
167068abb2bSMichael Grosse        /** @var OptGroup $optGroup */
1684eed441dSMichael Grosse        foreach ($this->optGroups as $optGroup) {
1699c3fca6dSMichael Grosse            $value_exists = $optGroup->storeValue($value) || $value_exists;
17045082b9eSMichael Grosse            if ($value_exists) {
171068abb2bSMichael Grosse                $value = null;
172068abb2bSMichael Grosse            }
173238a072bSMichael Grosse        }
174238a072bSMichael Grosse        return $value_exists;
175238a072bSMichael Grosse    }
176238a072bSMichael Grosse
177238a072bSMichael Grosse    /**
1788638ead5SAndreas Gohr     * Create the HTML for the select it self
1798638ead5SAndreas Gohr     *
1808638ead5SAndreas Gohr     * @return string
1818638ead5SAndreas Gohr     */
1828638ead5SAndreas Gohr    protected function mainElementHTML() {
1838638ead5SAndreas Gohr        if($this->useInput) $this->prefillInput();
1848638ead5SAndreas Gohr
1858638ead5SAndreas Gohr        $html = '<select ' . buildAttributes($this->attrs()) . '>';
186*64159a61SAndreas Gohr        $html = array_reduce(
187*64159a61SAndreas Gohr            $this->optGroups,
188*64159a61SAndreas Gohr            function ($html, OptGroup $optGroup) {
189*64159a61SAndreas Gohr                return $html . $optGroup->toHTML();
190*64159a61SAndreas Gohr            },
191*64159a61SAndreas Gohr            $html
192*64159a61SAndreas Gohr        );
1938638ead5SAndreas Gohr        $html .= '</select>';
1948638ead5SAndreas Gohr
1958638ead5SAndreas Gohr        return $html;
1968638ead5SAndreas Gohr    }
1978638ead5SAndreas Gohr
1988638ead5SAndreas Gohr}
199