xref: /plugin/struct/types/Dropdown.php (revision 56dced67cbc4a0cff463b3304e10eda0eb93ee6c)
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, $label, $ismulti, $tid) {
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        }
199        return $value;
200    }
201
202    /**
203     * Merge with lookup table
204     *
205     * @param QueryBuilder $QB
206     * @param string $tablealias
207     * @param string $colname
208     * @param string $alias
209     */
210    public function select(QueryBuilder $QB, $tablealias, $colname, $alias) {
211        if(!$this->usesLookup()) {
212            parent::select($QB, $tablealias, $colname, $alias);
213            return;
214        }
215
216        $schema = 'data_'.$this->schema->getTable();
217        $field = $this->column->getColName();
218
219        $rightalias = $QB->generateTableAlias();
220        $QB->addLeftJoin($tablealias, $schema, $rightalias, "$tablealias.$colname = $rightalias.pid");
221        $this->column->getType()->select($QB, $rightalias, $field, $alias);
222        $sql = $QB->getSelectStatement($alias);
223        $QB->addSelectStatement("JSON($tablealias.$colname, $sql)", $alias);
224    }
225
226    /**
227     * Compare against lookup table
228     *
229     * @param QueryBuilder $QB
230     * @param string $tablealias
231     * @param string $colname
232     * @param string $comp
233     * @param string|\string[] $value
234     * @param string $op
235     */
236    public function filter(QueryBuilder $QB, $tablealias, $colname, $comp, $value, $op) {
237        if(!$this->usesLookup()) {
238            parent::filter($QB, $tablealias, $colname, $comp, $value, $op);
239            return;
240        }
241
242        $schema = 'data_'.$this->schema->getTable();
243        $field = $this->column->getColName();
244
245        // compare against lookup field
246        $rightalias = $QB->generateTableAlias();
247        $QB->addLeftJoin($tablealias, $schema, $rightalias, "$tablealias.$colname = $rightalias.pid");
248        $this->column->getType()->filter($QB, $rightalias, $field, $comp, $value, $op);
249    }
250
251    /**
252     * Sort by lookup table
253     *
254     * @param QueryBuilder $QB
255     * @param string $tablealias
256     * @param string $colname
257     * @param string $order
258     */
259    public function sort(QueryBuilder $QB, $tablealias, $colname, $order) {
260        if(!$this->usesLookup()) {
261            parent::sort($QB, $tablealias, $colname, $order);
262            return;
263        }
264
265        $schema = 'data_'.$this->schema->getTable();
266        $field = $this->column->getColName();
267
268        $rightalias = $QB->generateTableAlias();
269        $QB->addLeftJoin($tablealias, $schema, $rightalias, "$tablealias.$colname = $rightalias.pid");
270        $this->column->getType()->sort($QB, $rightalias, $field, $order);
271    }
272
273}
274