xref: /dokuwiki/inc/Form/DropdownElement.php (revision 5a10fbce331993a3912ae78e8acb99ca44be19c1)
18638ead5SAndreas Gohr<?php
29d01c1d9SSatoshi Sahara
38638ead5SAndreas Gohrnamespace dokuwiki\Form;
48638ead5SAndreas Gohr
58638ead5SAndreas Gohr/**
68638ead5SAndreas Gohr * Class DropdownElement
78638ead5SAndreas Gohr *
8*5a10fbceSAndreas Gohr * Represents a HTML select. Please not that prefilling with input data only works for single values.
98638ead5SAndreas Gohr *
108638ead5SAndreas Gohr * @package dokuwiki\Form
118638ead5SAndreas Gohr */
129d01c1d9SSatoshi Saharaclass DropdownElement extends InputElement
139d01c1d9SSatoshi Sahara{
14238a072bSMichael Grosse    /** @var array OptGroup[] */
15*5a10fbceSAndreas Gohr    protected $optGroups = [];
16*5a10fbceSAndreas Gohr
17*5a10fbceSAndreas Gohr    /** @var string[] the currently set values */
18*5a10fbceSAndreas Gohr    protected $values = [];
198638ead5SAndreas Gohr
208638ead5SAndreas Gohr    /**
218638ead5SAndreas Gohr     * @param string $name The name of this form element
224eed441dSMichael Grosse     * @param array $options The available options
238638ead5SAndreas Gohr     * @param string $label The label text for this element (will be autoescaped)
248638ead5SAndreas Gohr     */
259d01c1d9SSatoshi Sahara    public function __construct($name, $options, $label = '')
269d01c1d9SSatoshi Sahara    {
278638ead5SAndreas Gohr        parent::__construct('dropdown', $name, $label);
28693978b1SMichael Grosse        $this->rmattr('type');
294eed441dSMichael Grosse        $this->optGroups[''] = new OptGroup(null, $options);
30238a072bSMichael Grosse        $this->val('');
31238a072bSMichael Grosse    }
32238a072bSMichael Grosse
33238a072bSMichael Grosse    /**
344eed441dSMichael Grosse     * Add an `<optgroup>` and respective options
354eed441dSMichael Grosse     *
364eed441dSMichael Grosse     * @param string $label
374eed441dSMichael Grosse     * @param array $options
384eed441dSMichael Grosse     * @return OptGroup a reference to the added optgroup
39*5a10fbceSAndreas Gohr     * @throws \InvalidArgumentException
40238a072bSMichael Grosse     */
419d01c1d9SSatoshi Sahara    public function addOptGroup($label, $options)
429d01c1d9SSatoshi Sahara    {
43238a072bSMichael Grosse        if (empty($label)) {
44238a072bSMichael Grosse            throw new \InvalidArgumentException(hsc('<optgroup> must have a label!'));
45238a072bSMichael Grosse        }
464eed441dSMichael Grosse        $this->optGroups[$label] = new OptGroup($label, $options);
47238a072bSMichael Grosse        return end($this->optGroups);
48238a072bSMichael Grosse    }
49238a072bSMichael Grosse
50238a072bSMichael Grosse    /**
51238a072bSMichael Grosse     * Set or get the optgroups of an Dropdown-Element.
52238a072bSMichael Grosse     *
53238a072bSMichael Grosse     * optgroups have to be given as associative array
54238a072bSMichael Grosse     *   * the key being the label of the group
55*5a10fbceSAndreas Gohr     *   * the value being an array of options as defined in @param null|array $optGroups
562bd1d2c7SMichael Grosse     * @return OptGroup[]|DropdownElement
57*5a10fbceSAndreas Gohr     * @see OptGroup::options()
58*5a10fbceSAndreas Gohr     *
59238a072bSMichael Grosse     */
609d01c1d9SSatoshi Sahara    public function optGroups($optGroups = null)
619d01c1d9SSatoshi Sahara    {
62238a072bSMichael Grosse        if ($optGroups === null) {
63238a072bSMichael Grosse            return $this->optGroups;
64238a072bSMichael Grosse        }
65238a072bSMichael Grosse        if (!is_array($optGroups)) {
66238a072bSMichael Grosse            throw new \InvalidArgumentException(hsc('Argument must be an associative array of label => [options]!'));
67238a072bSMichael Grosse        }
68238a072bSMichael Grosse        $this->optGroups = array();
69238a072bSMichael Grosse        foreach ($optGroups as $label => $options) {
70238a072bSMichael Grosse            $this->addOptGroup($label, $options);
71238a072bSMichael Grosse        }
722bd1d2c7SMichael Grosse        return $this;
738638ead5SAndreas Gohr    }
748638ead5SAndreas Gohr
758638ead5SAndreas Gohr    /**
768638ead5SAndreas Gohr     * Get or set the options of the Dropdown
778638ead5SAndreas Gohr     *
788638ead5SAndreas Gohr     * Options can be given as associative array (value => label) or as an
79795955b2SMichael Grosse     * indexd array (label = value) or as an array of arrays. In the latter
80795955b2SMichael Grosse     * case an element has to look as follows:
81795955b2SMichael Grosse     * option-value => array (
82795955b2SMichael Grosse     *                 'label' => option-label,
83795955b2SMichael Grosse     *                 'attrs' => array (
84795955b2SMichael Grosse     *                                    attr-key => attr-value, ...
85795955b2SMichael Grosse     *                                  )
86795955b2SMichael Grosse     *                 )
878638ead5SAndreas Gohr     *
888638ead5SAndreas Gohr     * @param null|array $options
898638ead5SAndreas Gohr     * @return $this|array
908638ead5SAndreas Gohr     */
919d01c1d9SSatoshi Sahara    public function options($options = null)
929d01c1d9SSatoshi Sahara    {
93238a072bSMichael Grosse        if ($options === null) {
944eed441dSMichael Grosse            return $this->optGroups['']->options();
958638ead5SAndreas Gohr        }
964eed441dSMichael Grosse        $this->optGroups[''] = new OptGroup(null, $options);
978638ead5SAndreas Gohr        return $this;
988638ead5SAndreas Gohr    }
998638ead5SAndreas Gohr
1008638ead5SAndreas Gohr    /**
1018638ead5SAndreas Gohr     * Get or set the current value
1028638ead5SAndreas Gohr     *
1038638ead5SAndreas Gohr     * When setting a value that is not defined in the options, the value is ignored
1048638ead5SAndreas Gohr     * and the first option's value is selected instead
1058638ead5SAndreas Gohr     *
106*5a10fbceSAndreas Gohr     * @param null|string|string[] $value The value to set
107*5a10fbceSAndreas Gohr     * @return $this|string|string[]
1088638ead5SAndreas Gohr     */
1099d01c1d9SSatoshi Sahara    public function val($value = null)
1109d01c1d9SSatoshi Sahara    {
111*5a10fbceSAndreas Gohr        // getter
112*5a10fbceSAndreas Gohr        if ($value === null) {
113*5a10fbceSAndreas Gohr            if (isset($this->attributes['multiple'])) {
114*5a10fbceSAndreas Gohr                return $this->values;
1158638ead5SAndreas Gohr            } else {
116*5a10fbceSAndreas Gohr                return $this->values[0];
117*5a10fbceSAndreas Gohr            }
118*5a10fbceSAndreas Gohr        }
119*5a10fbceSAndreas Gohr
120*5a10fbceSAndreas Gohr        // setter
121*5a10fbceSAndreas Gohr        $this->values = $this->setValuesInOptGroups((array) $value);
122*5a10fbceSAndreas Gohr        if(!$this->values) {
1238638ead5SAndreas Gohr            // unknown value set, select first option instead
124*5a10fbceSAndreas Gohr            $this->values = $this->setValuesInOptGroups((array) $this->getFirstOptionKey());
1258638ead5SAndreas Gohr        }
1268638ead5SAndreas Gohr
1278638ead5SAndreas Gohr        return $this;
1288638ead5SAndreas Gohr    }
1298638ead5SAndreas Gohr
1308638ead5SAndreas Gohr    /**
131*5a10fbceSAndreas Gohr     * Returns the first option's key
132238a072bSMichael Grosse     *
133238a072bSMichael Grosse     * @return string
134238a072bSMichael Grosse     */
135*5a10fbceSAndreas Gohr    protected function getFirstOptionKey()
1369d01c1d9SSatoshi Sahara    {
137238a072bSMichael Grosse        $options = $this->options();
138238a072bSMichael Grosse        if (!empty($options)) {
1397b6bf7a3SMichael Grosse            $keys = array_keys($options);
1407b6bf7a3SMichael Grosse            return (string)array_shift($keys);
141238a072bSMichael Grosse        }
142238a072bSMichael Grosse        foreach ($this->optGroups as $optGroup) {
143238a072bSMichael Grosse            $options = $optGroup->options();
144238a072bSMichael Grosse            if (!empty($options)) {
1457b6bf7a3SMichael Grosse                $keys = array_keys($options);
1467b6bf7a3SMichael Grosse                return (string)array_shift($keys);
147238a072bSMichael Grosse            }
148238a072bSMichael Grosse        }
149*5a10fbceSAndreas Gohr
150*5a10fbceSAndreas Gohr        return ''; // should not happen
151238a072bSMichael Grosse    }
152238a072bSMichael Grosse
153238a072bSMichael Grosse    /**
154238a072bSMichael Grosse     * Set the value in the OptGroups, including the optgroup for the options without optgroup.
155238a072bSMichael Grosse     *
156*5a10fbceSAndreas Gohr     * @param string[] $values The values to be set
157*5a10fbceSAndreas Gohr     * @return string[] The values actually set
158238a072bSMichael Grosse     */
159*5a10fbceSAndreas Gohr    protected function setValuesInOptGroups($values)
1609d01c1d9SSatoshi Sahara    {
161*5a10fbceSAndreas Gohr        $valueset = [];
162*5a10fbceSAndreas Gohr
163068abb2bSMichael Grosse        /** @var OptGroup $optGroup */
1644eed441dSMichael Grosse        foreach ($this->optGroups as $optGroup) {
165*5a10fbceSAndreas Gohr            $found = $optGroup->storeValues($values);
166*5a10fbceSAndreas Gohr            $values = array_diff($values, $found);
167*5a10fbceSAndreas Gohr            $valueset = array_merge($valueset, $found);
168068abb2bSMichael Grosse        }
169*5a10fbceSAndreas Gohr
170*5a10fbceSAndreas Gohr        return $valueset;
171238a072bSMichael Grosse    }
172238a072bSMichael Grosse
173238a072bSMichael Grosse    /**
1748638ead5SAndreas Gohr     * Create the HTML for the select it self
1758638ead5SAndreas Gohr     *
1768638ead5SAndreas Gohr     * @return string
1778638ead5SAndreas Gohr     */
1789d01c1d9SSatoshi Sahara    protected function mainElementHTML()
1799d01c1d9SSatoshi Sahara    {
180*5a10fbceSAndreas Gohr        $attr = $this->attrs();
181*5a10fbceSAndreas Gohr        if (isset($attr['multiple'])) {
182*5a10fbceSAndreas Gohr            // use array notation when multiple values are allowed
183*5a10fbceSAndreas Gohr            $attr['name'] .= '[]';
184*5a10fbceSAndreas Gohr        } elseif ($this->useInput) {
185*5a10fbceSAndreas Gohr            // prefilling is only supported for non-multi fields
186*5a10fbceSAndreas Gohr            $this->prefillInput();
187*5a10fbceSAndreas Gohr        }
1888638ead5SAndreas Gohr
189*5a10fbceSAndreas Gohr        $html = '<select ' . buildAttributes($attr) . '>';
19064159a61SAndreas Gohr        $html = array_reduce(
19164159a61SAndreas Gohr            $this->optGroups,
19264159a61SAndreas Gohr            function ($html, OptGroup $optGroup) {
19364159a61SAndreas Gohr                return $html . $optGroup->toHTML();
19464159a61SAndreas Gohr            },
19564159a61SAndreas Gohr            $html
19664159a61SAndreas Gohr        );
1978638ead5SAndreas Gohr        $html .= '</select>';
1988638ead5SAndreas Gohr
1998638ead5SAndreas Gohr        return $html;
2008638ead5SAndreas Gohr    }
2018638ead5SAndreas Gohr
2028638ead5SAndreas Gohr}
203