xref: /dokuwiki/inc/Form/DropdownElement.php (revision 45082b9e22a85166b4866cd8d8fe4da24ddcfd5f)
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 OptGroup */
14238a072bSMichael Grosse    protected $options = null;
15238a072bSMichael Grosse
16238a072bSMichael Grosse    /** @var array OptGroup[] */
17238a072bSMichael 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');
29238a072bSMichael Grosse        $this->options = new OptGroup(null, $options);
30238a072bSMichael Grosse        $this->val('');
31238a072bSMichael Grosse    }
32238a072bSMichael Grosse
33238a072bSMichael Grosse    /**
34238a072bSMichael Grosse     * @param $label
35238a072bSMichael Grosse     * @param $options
36068abb2bSMichael Grosse     * @return OptGroup
37238a072bSMichael Grosse     * @throws \Exception
38238a072bSMichael Grosse     */
39238a072bSMichael Grosse    public function addOptGroup($label, $options) {
40238a072bSMichael Grosse        if (empty($label)) {
41238a072bSMichael Grosse            throw new \InvalidArgumentException(hsc('<optgroup> must have a label!'));
42238a072bSMichael Grosse        }
43238a072bSMichael Grosse        $this->optGroups[] = new OptGroup($label, $options);
44238a072bSMichael Grosse        return end($this->optGroups);
45238a072bSMichael Grosse    }
46238a072bSMichael Grosse
47238a072bSMichael Grosse    /**
48238a072bSMichael Grosse     * Set or get the optgroups of an Dropdown-Element.
49238a072bSMichael Grosse     *
50238a072bSMichael Grosse     * optgroups have to be given as associative array
51238a072bSMichael Grosse     *   * the key being the label of the group
52238a072bSMichael Grosse     *   * the value being an array of options as defined in @see OptGroup::options()
53238a072bSMichael Grosse     *
54238a072bSMichael Grosse     * @param null|array $optGroups
552bd1d2c7SMichael Grosse     * @return OptGroup[]|DropdownElement
56238a072bSMichael Grosse     */
57238a072bSMichael Grosse    public function optGroups($optGroups = null) {
58238a072bSMichael Grosse        if($optGroups === null) {
59238a072bSMichael Grosse            return $this->optGroups;
60238a072bSMichael Grosse        }
61238a072bSMichael Grosse        if (!is_array($optGroups)) {
62238a072bSMichael Grosse            throw new \InvalidArgumentException(hsc('Argument must be an associative array of label => [options]!'));
63238a072bSMichael Grosse        }
64238a072bSMichael Grosse        $this->optGroups = array();
65238a072bSMichael Grosse        foreach ($optGroups as $label => $options) {
66238a072bSMichael Grosse            $this->addOptGroup($label, $options);
67238a072bSMichael Grosse        }
682bd1d2c7SMichael Grosse        return $this;
698638ead5SAndreas Gohr    }
708638ead5SAndreas Gohr
718638ead5SAndreas Gohr    /**
728638ead5SAndreas Gohr     * Get or set the options of the Dropdown
738638ead5SAndreas Gohr     *
748638ead5SAndreas Gohr     * Options can be given as associative array (value => label) or as an
75795955b2SMichael Grosse     * indexd array (label = value) or as an array of arrays. In the latter
76795955b2SMichael Grosse     * case an element has to look as follows:
77795955b2SMichael Grosse     * option-value => array (
78795955b2SMichael Grosse     *                 'label' => option-label,
79795955b2SMichael Grosse     *                 'attrs' => array (
80795955b2SMichael Grosse     *                                    attr-key => attr-value, ...
81795955b2SMichael Grosse     *                                  )
82795955b2SMichael Grosse     *                 )
838638ead5SAndreas Gohr     *
848638ead5SAndreas Gohr     * @param null|array $options
858638ead5SAndreas Gohr     * @return $this|array
868638ead5SAndreas Gohr     */
878638ead5SAndreas Gohr    public function options($options = null) {
88238a072bSMichael Grosse        if ($options === null) {
89238a072bSMichael Grosse            return $this->options->options();
908638ead5SAndreas Gohr        }
91238a072bSMichael Grosse        $this->options = new OptGroup(null, $options);
928638ead5SAndreas Gohr        return $this;
938638ead5SAndreas Gohr    }
948638ead5SAndreas Gohr
958638ead5SAndreas Gohr    /**
968638ead5SAndreas Gohr     * Gets or sets an attribute
978638ead5SAndreas Gohr     *
988638ead5SAndreas Gohr     * When no $value is given, the current content of the attribute is returned.
998638ead5SAndreas Gohr     * An empty string is returned for unset attributes.
1008638ead5SAndreas Gohr     *
1018638ead5SAndreas Gohr     * When a $value is given, the content is set to that value and the Element
1028638ead5SAndreas Gohr     * itself is returned for easy chaining
1038638ead5SAndreas Gohr     *
1048638ead5SAndreas Gohr     * @param string $name Name of the attribute to access
1058638ead5SAndreas Gohr     * @param null|string $value New value to set
1068638ead5SAndreas Gohr     * @return string|$this
1078638ead5SAndreas Gohr     */
1088638ead5SAndreas Gohr    public function attr($name, $value = null) {
1098638ead5SAndreas Gohr        if(strtolower($name) == 'multiple') {
1108638ead5SAndreas Gohr            throw new \InvalidArgumentException('Sorry, the dropdown element does not support the "multiple" attribute');
1118638ead5SAndreas Gohr        }
1128638ead5SAndreas Gohr        return parent::attr($name, $value);
1138638ead5SAndreas Gohr    }
1148638ead5SAndreas Gohr
1158638ead5SAndreas Gohr    /**
1168638ead5SAndreas Gohr     * Get or set the current value
1178638ead5SAndreas Gohr     *
1188638ead5SAndreas Gohr     * When setting a value that is not defined in the options, the value is ignored
1198638ead5SAndreas Gohr     * and the first option's value is selected instead
1208638ead5SAndreas Gohr     *
1218638ead5SAndreas Gohr     * @param null|string $value The value to set
1228638ead5SAndreas Gohr     * @return $this|string
1238638ead5SAndreas Gohr     */
1248638ead5SAndreas Gohr    public function val($value = null) {
1258638ead5SAndreas Gohr        if($value === null) return $this->value;
1268638ead5SAndreas Gohr
127238a072bSMichael Grosse        $value_exists = $this->setValueInOptGroups($value);
128238a072bSMichael Grosse
129238a072bSMichael Grosse        if($value_exists) {
1308638ead5SAndreas Gohr            $this->value = $value;
1318638ead5SAndreas Gohr        } else {
1328638ead5SAndreas Gohr            // unknown value set, select first option instead
133238a072bSMichael Grosse            $this->value = $this->getFirstOption();
134238a072bSMichael Grosse            $this->setValueInOptGroups($this->value);
1358638ead5SAndreas Gohr        }
1368638ead5SAndreas Gohr
1378638ead5SAndreas Gohr        return $this;
1388638ead5SAndreas Gohr    }
1398638ead5SAndreas Gohr
1408638ead5SAndreas Gohr    /**
141238a072bSMichael Grosse     * Returns the first options as it will be rendered in HTML
142238a072bSMichael Grosse     *
143238a072bSMichael Grosse     * @return string
144238a072bSMichael Grosse     */
145238a072bSMichael Grosse    protected function getFirstOption() {
146238a072bSMichael Grosse        $options = $this->options();
147238a072bSMichael Grosse        if (!empty($options)) {
148238a072bSMichael Grosse            return (string) array_shift(array_keys($options));
149238a072bSMichael Grosse        }
150238a072bSMichael Grosse        foreach ($this->optGroups as $optGroup) {
151238a072bSMichael Grosse            $options = $optGroup->options();
152238a072bSMichael Grosse            if (!empty($options)) {
153238a072bSMichael Grosse                return (string) array_shift(array_keys($options));
154238a072bSMichael Grosse            }
155238a072bSMichael Grosse        }
156238a072bSMichael Grosse    }
157238a072bSMichael Grosse
158238a072bSMichael Grosse    /**
159238a072bSMichael Grosse     * Set the value in the OptGroups, including the optgroup for the options without optgroup.
160238a072bSMichael Grosse     *
161238a072bSMichael Grosse     * @param string $value
162238a072bSMichael Grosse     * @return bool
163238a072bSMichael Grosse     */
164238a072bSMichael Grosse    protected function setValueInOptGroups($value) {
165068abb2bSMichael Grosse        $value_exists = false;
166068abb2bSMichael Grosse        /** @var OptGroup $optGroup */
167068abb2bSMichael Grosse        foreach (array_merge(array($this->options), $this->optGroups) as $optGroup) {
168238a072bSMichael Grosse            $value_exists = $optGroup->setValue($value) || $value_exists;
169*45082b9eSMichael Grosse            if ($value_exists) {
170068abb2bSMichael Grosse                $value = null;
171068abb2bSMichael Grosse            }
172238a072bSMichael Grosse        }
173238a072bSMichael Grosse        return $value_exists;
174238a072bSMichael Grosse    }
175238a072bSMichael Grosse
176238a072bSMichael Grosse    /**
1778638ead5SAndreas Gohr     * Create the HTML for the select it self
1788638ead5SAndreas Gohr     *
1798638ead5SAndreas Gohr     * @return string
1808638ead5SAndreas Gohr     */
1818638ead5SAndreas Gohr    protected function mainElementHTML() {
1828638ead5SAndreas Gohr        if($this->useInput) $this->prefillInput();
1838638ead5SAndreas Gohr
1848638ead5SAndreas Gohr        $html = '<select ' . buildAttributes($this->attrs()) . '>';
185238a072bSMichael Grosse        $html .= $this->options->toHTML();
186238a072bSMichael Grosse        $html = array_reduce($this->optGroups, function($html, OptGroup $optGroup) {return $html . $optGroup->toHTML();}, $html);
1878638ead5SAndreas Gohr        $html .= '</select>';
1888638ead5SAndreas Gohr
1898638ead5SAndreas Gohr        return $html;
1908638ead5SAndreas Gohr    }
1918638ead5SAndreas Gohr
1928638ead5SAndreas Gohr}
193