xref: /plugin/struct/types/Lookup.php (revision 7f803aa873894c400484b80e8b543a49fdf1f9d5)
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\QueryBuilderWhere;
7use dokuwiki\plugin\struct\meta\Schema;
8use dokuwiki\plugin\struct\meta\Search;
9use dokuwiki\plugin\struct\meta\SummaryColumn;
10use dokuwiki\plugin\struct\meta\Value;
11use dokuwiki\plugin\struct\meta\PageColumn;
12use dokuwiki\plugin\struct\meta\RevisionColumn;
13use dokuwiki\plugin\struct\meta\UserColumn;
14use dokuwiki\plugin\struct\meta\RowColumn;
15
16class Lookup extends Dropdown {
17
18    protected $config = array(
19        'schema' => '',
20        'field' => ''
21    );
22
23    /** @var  Column caches the referenced column */
24    protected $column = null;
25
26    /**
27     * Dropdown constructor.
28     *
29     * @param array|null $config
30     * @param string $label
31     * @param bool $ismulti
32     * @param int $tid
33     */
34    public function __construct($config = null, $label = '', $ismulti = false, $tid = 0) {
35        parent::__construct($config, $label, $ismulti, $tid);
36        $this->config['schema'] = Schema::cleanTableName($this->config['schema']);
37    }
38
39    /**
40     * Get the configured loojup column
41     *
42     * @return Column|false
43     */
44    protected function getLookupColumn() {
45        if($this->column !== null) return $this->column;
46        $this->column = $this->getColumn($this->config['schema'], $this->config['field']);
47        return $this->column;
48    }
49
50    /**
51     * Gets the given column, applies language place holder
52     *
53     * @param string $table
54     * @param string $infield
55     * @return Column|false
56     */
57    protected function getColumn($table, $infield) {
58        global $conf;
59
60        $table = new Schema($table);
61        if(!$table->getId()) {
62            // schema does not exist
63            msg(sprintf('Schema %s does not exist', $table), -1);
64            return false;
65        }
66
67        // apply language replacement
68        $field = str_replace('$LANG', $conf['lang'], $infield);
69        $column = $table->findColumn($field);
70        if(!$column) {
71            $field = str_replace('$LANG', 'en', $infield); // fallback to en
72            $column = $table->findColumn($field);
73        }
74        if(!$column) {
75            if($infield == '%pageid%') {
76                $column = new PageColumn(0, new Page(), $table);
77            }
78            if($infield == '%title%') {
79                $column = new PageColumn(0, new Page(array('usetitles' => true)), $table);
80            }
81            if($infield == '%lastupdate%') {
82                $column = new RevisionColumn(0, new DateTime(), $table);
83            }
84            if ($infield == '%lasteditor%') {
85                $column = new UserColumn(0, new User(), $table);
86            }
87            if ($infield == '%lastsummary%') {
88                return new SummaryColumn(0, new AutoSummary(), $table);
89            }
90            if($infield == '%rowid%') {
91                $column = new RowColumn(0, new Decimal(), $table);
92            }
93        }
94        if(!$column) {
95            // field does not exist
96            msg(sprintf('Field %s.%s does not exist', $table, $infield), -1);
97            return false;
98        }
99
100        if($column->isMulti()) {
101            // field is multi
102            msg(sprintf('Field %s.%s is a multi field - not allowed for lookup', $table, $field), -1);
103            return false;
104        }
105
106        return $column;
107    }
108
109    /**
110     * Creates the options array
111     *
112     * @return array
113     */
114    protected function getOptions() {
115        $schema = $this->config['schema'];
116        $column = $this->getLookupColumn();
117        if(!$column) return array();
118        $field = $column->getLabel();
119
120        $search = new Search();
121        $search->addSchema($schema);
122        $search->addColumn($field);
123        $search->addSort($field);
124        $result = $search->execute();
125        $rids = $search->getRids();
126        $len = count($result);
127
128        /** @var Value[][] $result */
129        $options = array('' => '');
130        for($i = 0; $i < $len; $i++) {
131            $options[$rids[$i]] = $result[$i][0]->getDisplayValue();
132        }
133        return $options;
134    }
135
136    /**
137     * Render using linked field
138     *
139     * @param int|string $value
140     * @param \Doku_Renderer $R
141     * @param string $mode
142     * @return bool
143     */
144    public function renderValue($value, \Doku_Renderer $R, $mode) {
145        list(, $value) = json_decode($value);
146        $column = $this->getLookupColumn();
147        if(!$column) return false;
148        return $column->getType()->renderValue($value, $R, $mode);
149    }
150
151    /**
152     * Render using linked field
153     *
154     * @param \int[]|\string[] $values
155     * @param \Doku_Renderer $R
156     * @param string $mode
157     * @return bool
158     */
159    public function renderMultiValue($values, \Doku_Renderer $R, $mode) {
160        $values = array_map(
161            function ($val) {
162                list(, $val) = json_decode($val);
163                return $val;
164            }, $values
165        );
166        $column = $this->getLookupColumn();
167        if(!$column) return false;
168        return $column->getType()->renderMultiValue($values, $R, $mode);
169    }
170
171    /**
172     * @param string $value
173     * @return string
174     */
175    public function rawValue($value) {
176        list($value) = json_decode($value);
177        return $value;
178    }
179
180    /**
181     * @param string $value
182     * @return string
183     */
184    public function displayValue($value) {
185        list(, $value) = json_decode($value);
186        $column = $this->getLookupColumn();
187        if($column) {
188            return $column->getType()->displayValue($value);
189        } else {
190            return '';
191        }
192    }
193
194    /**
195     * This is the value to be used as argument to a filter for another column.
196     *
197     * In a sense this is the counterpart to the @see filter() function
198     *
199     * @param string $value
200     *
201     * @return string
202     */
203    public function compareValue($value) {
204        list(, $value) = json_decode($value);
205        $column = $this->getLookupColumn();
206        if($column) {
207            return $column->getType()->rawValue($value);
208        } else {
209            return '';
210        }
211    }
212
213
214    /**
215     * Merge with lookup table
216     *
217     * @param QueryBuilder $QB
218     * @param string $tablealias
219     * @param string $colname
220     * @param string $alias
221     */
222    public function select(QueryBuilder $QB, $tablealias, $colname, $alias) {
223        $schema = 'data_' . $this->config['schema'];
224        $column = $this->getLookupColumn();
225        if(!$column) {
226            parent::select($QB, $tablealias, $colname, $alias);
227            return;
228        }
229
230        $field = $column->getColName();
231        $rightalias = $QB->generateTableAlias();
232        $QB->addLeftJoin(
233            $tablealias, $schema, $rightalias,
234            "$tablealias.$colname = $rightalias.rid AND $rightalias.latest = 1"
235        );
236        $column->getType()->select($QB, $rightalias, $field, $alias);
237        $sql = $QB->getSelectStatement($alias);
238        $QB->addSelectStatement("STRUCT_JSON($tablealias.$colname, $sql)", $alias);
239    }
240
241    /**
242     * Compare against lookup table
243     *
244     * @param QueryBuilderWhere $add
245     * @param string $tablealias
246     * @param string $colname
247     * @param string $comp
248     * @param string|\string[] $value
249     * @param string $op
250     */
251    public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op) {
252        $schema = 'data_' . $this->config['schema'];
253        $column = $this->getLookupColumn();
254        if(!$column) {
255            parent::filter($add, $tablealias, $colname, $comp, $value, $op);
256            return;
257        }
258        $field = $column->getColName();
259
260        // compare against lookup field
261        $QB = $add->getQB();
262        $rightalias = $QB->generateTableAlias();
263        $QB->addLeftJoin(
264            $tablealias, $schema, $rightalias,
265            "$tablealias.$colname = $rightalias.rid AND $rightalias.latest = 1"
266        );
267        $column->getType()->filter($add, $rightalias, $field, $comp, $value, $op);
268    }
269
270    /**
271     * Sort by lookup table
272     *
273     * @param QueryBuilder $QB
274     * @param string $tablealias
275     * @param string $colname
276     * @param string $order
277     */
278    public function sort(QueryBuilder $QB, $tablealias, $colname, $order) {
279        $schema = 'data_' . $this->config['schema'];
280        $column = $this->getLookupColumn();
281        if(!$column) {
282            parent::sort($QB, $tablealias, $colname, $order);
283            return;
284        }
285        $field = $column->getColName();
286
287        $rightalias = $QB->generateTableAlias();
288        $QB->addLeftJoin(
289            $tablealias, $schema, $rightalias,
290            "$tablealias.$colname = $rightalias.rid AND $rightalias.latest = 1"
291        );
292        $column->getType()->sort($QB, $rightalias, $field, $order);
293    }
294
295}
296