xref: /dokuwiki/inc/Form/DropdownElement.php (revision 9d01c1d91a93cf50f37d1486481a6493e98be821)
18638ead5SAndreas Gohr<?php
2*9d01c1d9SSatoshi Sahara
38638ead5SAndreas Gohrnamespace dokuwiki\Form;
48638ead5SAndreas Gohr
58638ead5SAndreas Gohr/**
68638ead5SAndreas Gohr * Class DropdownElement
78638ead5SAndreas Gohr *
88638ead5SAndreas Gohr * Represents a HTML select. Please note that this does not support multiple selected options!
98638ead5SAndreas Gohr *
108638ead5SAndreas Gohr * @package dokuwiki\Form
118638ead5SAndreas Gohr */
12*9d01c1d9SSatoshi Saharaclass DropdownElement extends InputElement
13*9d01c1d9SSatoshi Sahara{
14238a072bSMichael Grosse    /** @var array OptGroup[] */
15238a072bSMichael Grosse    protected $optGroups = array();
168638ead5SAndreas Gohr
178638ead5SAndreas Gohr    /**
188638ead5SAndreas Gohr     * @param string $name The name of this form element
194eed441dSMichael Grosse     * @param array  $options The available options
208638ead5SAndreas Gohr     * @param string $label The label text for this element (will be autoescaped)
218638ead5SAndreas Gohr     */
22*9d01c1d9SSatoshi Sahara    public function __construct($name, $options, $label = '')
23*9d01c1d9SSatoshi Sahara    {
248638ead5SAndreas Gohr        parent::__construct('dropdown', $name, $label);
25693978b1SMichael Grosse        $this->rmattr('type');
264eed441dSMichael Grosse        $this->optGroups[''] = new OptGroup(null, $options);
27238a072bSMichael Grosse        $this->val('');
28238a072bSMichael Grosse    }
29238a072bSMichael Grosse
30238a072bSMichael Grosse    /**
314eed441dSMichael Grosse     * Add an `<optgroup>` and respective options
324eed441dSMichael Grosse     *
334eed441dSMichael Grosse     * @param string $label
344eed441dSMichael Grosse     * @param array  $options
354eed441dSMichael Grosse     * @return OptGroup a reference to the added optgroup
36238a072bSMichael Grosse     * @throws \Exception
37238a072bSMichael Grosse     */
38*9d01c1d9SSatoshi Sahara    public function addOptGroup($label, $options)
39*9d01c1d9SSatoshi Sahara    {
40238a072bSMichael Grosse        if (empty($label)) {
41238a072bSMichael Grosse            throw new \InvalidArgumentException(hsc('<optgroup> must have a label!'));
42238a072bSMichael Grosse        }
434eed441dSMichael Grosse        $this->optGroups[$label] = 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     */
57*9d01c1d9SSatoshi Sahara    public function optGroups($optGroups = null)
58*9d01c1d9SSatoshi Sahara    {
59238a072bSMichael Grosse        if ($optGroups === null) {
60238a072bSMichael Grosse            return $this->optGroups;
61238a072bSMichael Grosse        }
62238a072bSMichael Grosse        if (!is_array($optGroups)) {
63238a072bSMichael Grosse            throw new \InvalidArgumentException(hsc('Argument must be an associative array of label => [options]!'));
64238a072bSMichael Grosse        }
65238a072bSMichael Grosse        $this->optGroups = array();
66238a072bSMichael Grosse        foreach ($optGroups as $label => $options) {
67238a072bSMichael Grosse            $this->addOptGroup($label, $options);
68238a072bSMichael Grosse        }
692bd1d2c7SMichael Grosse        return $this;
708638ead5SAndreas Gohr    }
718638ead5SAndreas Gohr
728638ead5SAndreas Gohr    /**
738638ead5SAndreas Gohr     * Get or set the options of the Dropdown
748638ead5SAndreas Gohr     *
758638ead5SAndreas Gohr     * Options can be given as associative array (value => label) or as an
76795955b2SMichael Grosse     * indexd array (label = value) or as an array of arrays. In the latter
77795955b2SMichael Grosse     * case an element has to look as follows:
78795955b2SMichael Grosse     * option-value => array (
79795955b2SMichael Grosse     *                 'label' => option-label,
80795955b2SMichael Grosse     *                 'attrs' => array (
81795955b2SMichael Grosse     *                                    attr-key => attr-value, ...
82795955b2SMichael Grosse     *                                  )
83795955b2SMichael Grosse     *                 )
848638ead5SAndreas Gohr     *
858638ead5SAndreas Gohr     * @param null|array $options
868638ead5SAndreas Gohr     * @return $this|array
878638ead5SAndreas Gohr     */
88*9d01c1d9SSatoshi Sahara    public function options($options = null)
89*9d01c1d9SSatoshi Sahara    {
90238a072bSMichael Grosse        if ($options === null) {
914eed441dSMichael Grosse            return $this->optGroups['']->options();
928638ead5SAndreas Gohr        }
934eed441dSMichael Grosse        $this->optGroups[''] = new OptGroup(null, $options);
948638ead5SAndreas Gohr        return $this;
958638ead5SAndreas Gohr    }
968638ead5SAndreas Gohr
978638ead5SAndreas Gohr    /**
988638ead5SAndreas Gohr     * Gets or sets an attribute
998638ead5SAndreas Gohr     *
1008638ead5SAndreas Gohr     * When no $value is given, the current content of the attribute is returned.
1018638ead5SAndreas Gohr     * An empty string is returned for unset attributes.
1028638ead5SAndreas Gohr     *
1038638ead5SAndreas Gohr     * When a $value is given, the content is set to that value and the Element
1048638ead5SAndreas Gohr     * itself is returned for easy chaining
1058638ead5SAndreas Gohr     *
1068638ead5SAndreas Gohr     * @param string $name Name of the attribute to access
1078638ead5SAndreas Gohr     * @param null|string $value New value to set
1088638ead5SAndreas Gohr     * @return string|$this
1098638ead5SAndreas Gohr     */
110*9d01c1d9SSatoshi Sahara    public function attr($name, $value = null)
111*9d01c1d9SSatoshi Sahara    {
1128638ead5SAndreas Gohr        if (strtolower($name) == 'multiple') {
11364159a61SAndreas Gohr            throw new \InvalidArgumentException(
11464159a61SAndreas Gohr                'Sorry, the dropdown element does not support the "multiple" attribute'
11564159a61SAndreas Gohr            );
1168638ead5SAndreas Gohr        }
1178638ead5SAndreas Gohr        return parent::attr($name, $value);
1188638ead5SAndreas Gohr    }
1198638ead5SAndreas Gohr
1208638ead5SAndreas Gohr    /**
1218638ead5SAndreas Gohr     * Get or set the current value
1228638ead5SAndreas Gohr     *
1238638ead5SAndreas Gohr     * When setting a value that is not defined in the options, the value is ignored
1248638ead5SAndreas Gohr     * and the first option's value is selected instead
1258638ead5SAndreas Gohr     *
1268638ead5SAndreas Gohr     * @param null|string $value The value to set
1278638ead5SAndreas Gohr     * @return $this|string
1288638ead5SAndreas Gohr     */
129*9d01c1d9SSatoshi Sahara    public function val($value = null)
130*9d01c1d9SSatoshi Sahara    {
1318638ead5SAndreas Gohr        if ($value === null) return $this->value;
1328638ead5SAndreas Gohr
133238a072bSMichael Grosse        $value_exists = $this->setValueInOptGroups($value);
134238a072bSMichael Grosse
135238a072bSMichael Grosse        if ($value_exists) {
1368638ead5SAndreas Gohr            $this->value = $value;
1378638ead5SAndreas Gohr        } else {
1388638ead5SAndreas Gohr            // unknown value set, select first option instead
139238a072bSMichael Grosse            $this->value = $this->getFirstOption();
140238a072bSMichael Grosse            $this->setValueInOptGroups($this->value);
1418638ead5SAndreas Gohr        }
1428638ead5SAndreas Gohr
1438638ead5SAndreas Gohr        return $this;
1448638ead5SAndreas Gohr    }
1458638ead5SAndreas Gohr
1468638ead5SAndreas Gohr    /**
147238a072bSMichael Grosse     * Returns the first options as it will be rendered in HTML
148238a072bSMichael Grosse     *
149238a072bSMichael Grosse     * @return string
150238a072bSMichael Grosse     */
151*9d01c1d9SSatoshi Sahara    protected function getFirstOption()
152*9d01c1d9SSatoshi Sahara    {
153238a072bSMichael Grosse        $options = $this->options();
154238a072bSMichael Grosse        if (!empty($options)) {
1557b6bf7a3SMichael Grosse            $keys = array_keys($options);
1567b6bf7a3SMichael Grosse            return (string) array_shift($keys);
157238a072bSMichael Grosse        }
158238a072bSMichael Grosse        foreach ($this->optGroups as $optGroup) {
159238a072bSMichael Grosse            $options = $optGroup->options();
160238a072bSMichael Grosse            if (!empty($options)) {
1617b6bf7a3SMichael Grosse                $keys = array_keys($options);
1627b6bf7a3SMichael Grosse                return (string) array_shift($keys);
163238a072bSMichael Grosse            }
164238a072bSMichael Grosse        }
165238a072bSMichael Grosse    }
166238a072bSMichael Grosse
167238a072bSMichael Grosse    /**
168238a072bSMichael Grosse     * Set the value in the OptGroups, including the optgroup for the options without optgroup.
169238a072bSMichael Grosse     *
170238a072bSMichael Grosse     * @param string $value
171238a072bSMichael Grosse     * @return bool
172238a072bSMichael Grosse     */
173*9d01c1d9SSatoshi Sahara    protected function setValueInOptGroups($value)
174*9d01c1d9SSatoshi Sahara    {
175068abb2bSMichael Grosse        $value_exists = false;
176068abb2bSMichael Grosse        /** @var OptGroup $optGroup */
1774eed441dSMichael Grosse        foreach ($this->optGroups as $optGroup) {
1789c3fca6dSMichael Grosse            $value_exists = $optGroup->storeValue($value) || $value_exists;
17945082b9eSMichael Grosse            if ($value_exists) {
180068abb2bSMichael Grosse                $value = null;
181068abb2bSMichael Grosse            }
182238a072bSMichael Grosse        }
183238a072bSMichael Grosse        return $value_exists;
184238a072bSMichael Grosse    }
185238a072bSMichael Grosse
186238a072bSMichael Grosse    /**
1878638ead5SAndreas Gohr     * Create the HTML for the select it self
1888638ead5SAndreas Gohr     *
1898638ead5SAndreas Gohr     * @return string
1908638ead5SAndreas Gohr     */
191*9d01c1d9SSatoshi Sahara    protected function mainElementHTML()
192*9d01c1d9SSatoshi Sahara    {
1938638ead5SAndreas Gohr        if ($this->useInput) $this->prefillInput();
1948638ead5SAndreas Gohr
1958638ead5SAndreas Gohr        $html = '<select ' . buildAttributes($this->attrs()) . '>';
19664159a61SAndreas Gohr        $html = array_reduce(
19764159a61SAndreas Gohr            $this->optGroups,
19864159a61SAndreas Gohr            function ($html, OptGroup $optGroup) {
19964159a61SAndreas Gohr                return $html . $optGroup->toHTML();
20064159a61SAndreas Gohr            },
20164159a61SAndreas Gohr            $html
20264159a61SAndreas Gohr        );
2038638ead5SAndreas Gohr        $html .= '</select>';
2048638ead5SAndreas Gohr
2058638ead5SAndreas Gohr        return $html;
2068638ead5SAndreas Gohr    }
2078638ead5SAndreas Gohr
2088638ead5SAndreas Gohr}
209