xref: /plugin/struct/types/Dropdown.php (revision 3eadf179cd787abb997d8a79eea67bef0c90286e)
1<?php
2namespace dokuwiki\plugin\struct\types;
3
4use dokuwiki\plugin\struct\meta\Column;
5use dokuwiki\plugin\struct\meta\QueryBuilder;
6use dokuwiki\plugin\struct\meta\Schema;
7use dokuwiki\plugin\struct\meta\Search;
8use dokuwiki\plugin\struct\meta\Value;
9
10class Dropdown extends AbstractBaseType {
11
12    protected $config = array(
13        'values' => 'one, two, three',
14        'schema' => '',
15        'field' => ''
16    );
17
18    /** @var Schema */
19    protected $schema = null;
20    /** @var Column */
21    protected $column = null;
22
23    /**
24     * Dropdown constructor.
25     *
26     * @param array|null $config
27     * @param string $label
28     * @param bool $ismulti
29     * @param int $tid
30     */
31    public function __construct($config = null, $label = '', $ismulti = false, $tid = 0) {
32        global $conf;
33
34        parent::__construct($config, $label, $ismulti, $tid);
35        $this->config['schema'] = Schema::cleanTableName($this->config['schema']);
36
37        if($this->usesLookup()) {
38            $this->schema = new Schema($this->config['schema']);
39            if(!$this->schema->getId()) {
40                // schema does not exist
41                msg(sprintf('Schema %s does not exist', $this->config['schema']), -1);
42                $this->schema = null;
43                $this->config['schema'] = '';
44                return;
45            }
46
47            // apply language replacement
48            $field = str_replace('$LANG', $conf['lang'], $this->config['field']);
49            $this->column = $this->schema->findColumn($field);
50            if(!$this->column) {
51                $field = str_replace('$LANG', 'en', $this->config['field']); // fallback to en
52                $this->column = $this->schema->findColumn($field);
53            }
54            if(!$this->column) {
55                // field does not exist
56                msg(sprintf('Field %s.%s does not exist', $this->config['schema'], $this->config['field']), -1);
57                $this->column = null;
58                $this->config['field'] = '';
59                return;
60            }
61
62            if($this->column->isMulti()) {
63                // field is multi
64                msg(sprintf('Field %s.%s is a multi field - not allowed for lookup', $this->config['schema'], $this->config['field']), -1);
65                $this->column = null;
66                $this->config['field'] = '';
67                return;
68            }
69        }
70    }
71
72    /**
73     * @return bool is this dropdown configured to use a lookup?
74     */
75    protected function usesLookup() {
76        return !blank($this->config['schema']) && !blank($this->config['field']);
77    }
78
79    /**
80     * Creates the options array
81     *
82     * @return array
83     */
84    protected function getOptions() {
85        if($this->usesLookup()) {
86            $options = $this->loadLookupData();
87        } else {
88            $options = explode(',', $this->config['values']);
89            $options = array_map('trim', $options);
90            $options = array_filter($options);
91            array_unshift($options, '');
92            $options = array_combine($options, $options);
93        }
94        return $options;
95    }
96
97    /**
98     * Loads all available lookup values
99     *
100     * @return array
101     */
102    protected function loadLookupData() {
103        $schema = $this->schema->getTable();
104        $field = $this->column->getLabel();
105
106        $search = new Search();
107        $search->addSchema($schema);
108        $search->addColumn($field);
109        $search->addSort($field);
110        $result = $search->execute();
111        $pids = $search->getPids();
112        $len = count($result);
113
114        /** @var Value[][] $result */
115        $options = array('' => '');
116        for($i = 0; $i < $len; $i++) {
117            $options[$pids[$i]] = $result[$i][0]->getDisplayValue();
118        }
119        return $options;
120    }
121
122    /**
123     * A Dropdown with a single value to pick
124     *
125     * @param string $name
126     * @param string $value
127     * @param bool $isRaw ignored
128     * @return string
129     */
130    public function valueEditor($name, $value, $isRaw = false) {
131        $class = 'struct_' . strtolower($this->getClass());
132
133        if(!$isRaw) $value = $this->rawValue($value);
134
135        $name = hsc($name);
136        $html = "<select name=\"$name\" class=\"$class\">";
137        foreach($this->getOptions() as $opt => $val) {
138            if($opt == $value) {
139                $selected = 'selected="selected"';
140            } else {
141                $selected = '';
142            }
143
144            $html .= "<option $selected value=\"" . hsc($opt) . "\">" . hsc($val) . '</option>';
145        }
146        $html .= '</select>';
147
148        return $html;
149    }
150
151    /**
152     * A dropdown that allows to pick multiple values
153     *
154     * @param string $name
155     * @param \string[] $values
156     * @return string
157     */
158    public function multiValueEditor($name, $values) {
159        $class = 'struct_' . strtolower($this->getClass());
160
161        $values = array_map(array($this, 'rawValue'), $values);
162
163        $name = hsc($name);
164        $html = "<select name=\"{$name}[]\" class=\"$class\" multiple=\"multiple\" size=\"5\">";
165        foreach($this->getOptions() as $opt) {
166            if(in_array($opt, $values)) {
167                $selected = 'selected="selected"';
168            } else {
169                $selected = '';
170            }
171
172            $html .= "<option $selected value=\"" . hsc($opt) . "\">" . hsc($opt) . '</option>';
173
174        }
175        $html .= '</select> ';
176        $html .= '<small>' . $this->getLang('multidropdown') . '</small>';
177        return $html;
178    }
179
180    /**
181     * @param string $value
182     * @return string
183     */
184    public function rawValue($value) {
185        if($this->usesLookup()) {
186            list($value) = json_decode($value);
187        }
188        return $value;
189    }
190
191    /**
192     * @param string $value
193     * @return string
194     */
195    public function displayValue($value) {
196        if($this->usesLookup()) {
197            list(, $value) = json_decode($value);
198            $value = $this->column->getType()->displayValue($value);
199        }
200        return $value;
201    }
202
203    /**
204     * Merge with lookup table
205     *
206     * @param QueryBuilder $QB
207     * @param string $tablealias
208     * @param string $colname
209     * @param string $alias
210     */
211    public function select(QueryBuilder $QB, $tablealias, $colname, $alias) {
212        if(!$this->usesLookup()) {
213            parent::select($QB, $tablealias, $colname, $alias);
214            return;
215        }
216
217        $schema = 'data_'.$this->schema->getTable();
218        $field = $this->column->getColName();
219
220        $rightalias = $QB->generateTableAlias();
221        $QB->addLeftJoin($tablealias, $schema, $rightalias, "$tablealias.$colname = $rightalias.pid");
222        $this->column->getType()->select($QB, $rightalias, $field, $alias);
223        $sql = $QB->getSelectStatement($alias);
224        $QB->addSelectStatement("JSON($tablealias.$colname, $sql)", $alias);
225    }
226
227    /**
228     * Compare against lookup table
229     *
230     * @param QueryBuilder $QB
231     * @param string $tablealias
232     * @param string $colname
233     * @param string $comp
234     * @param string|\string[] $value
235     * @param string $op
236     */
237    public function filter(QueryBuilder $QB, $tablealias, $colname, $comp, $value, $op) {
238        if(!$this->usesLookup()) {
239            parent::filter($QB, $tablealias, $colname, $comp, $value, $op);
240            return;
241        }
242
243        $schema = 'data_'.$this->schema->getTable();
244        $field = $this->column->getColName();
245
246        // compare against lookup field
247        $rightalias = $QB->generateTableAlias();
248        $QB->addLeftJoin($tablealias, $schema, $rightalias, "$tablealias.$colname = $rightalias.pid");
249        $this->column->getType()->filter($QB, $rightalias, $field, $comp, $value, $op);
250    }
251
252    /**
253     * Sort by lookup table
254     *
255     * @param QueryBuilder $QB
256     * @param string $tablealias
257     * @param string $colname
258     * @param string $order
259     */
260    public function sort(QueryBuilder $QB, $tablealias, $colname, $order) {
261        if(!$this->usesLookup()) {
262            parent::sort($QB, $tablealias, $colname, $order);
263            return;
264        }
265
266        $schema = 'data_'.$this->schema->getTable();
267        $field = $this->column->getColName();
268
269        $rightalias = $QB->generateTableAlias();
270        $QB->addLeftJoin($tablealias, $schema, $rightalias, "$tablealias.$colname = $rightalias.pid");
271        $this->column->getType()->sort($QB, $rightalias, $field, $order);
272    }
273
274}
275