xref: /plugin/struct/types/Lookup.php (revision c285142ae5a6cb06c7464d02e86d9ebea419e7ed)
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(!$table->isLookup()) {
76                if($infield == '%pageid%') {
77                    $column = new PageColumn(0, new Page(), $table);
78                }
79                if($infield == '%title%') {
80                    $column = new PageColumn(0, new Page(array('usetitles' => true)), $table);
81                }
82                if($infield == '%lastupdate%') {
83                    $column = new RevisionColumn(0, new DateTime(), $table);
84                }
85                if ($infield == '%lasteditor%') {
86                    $column = new UserColumn(0, new User(), $table);
87                }
88                if ($infield == '%lastsummary%') {
89                    return new SummaryColumn(0, new AutoSummary(), $table);
90                }
91            } else {
92                if($infield == '%rowid%') {
93                    $column = new RowColumn(0, new Decimal(), $table);
94                }
95            }
96        }
97        if(!$column) {
98            // field does not exist
99            msg(sprintf('Field %s.%s does not exist', $table, $infield), -1);
100            return false;
101        }
102
103        if($column->isMulti()) {
104            // field is multi
105            msg(sprintf('Field %s.%s is a multi field - not allowed for lookup', $table, $field), -1);
106            return false;
107        }
108
109        return $column;
110    }
111
112    /**
113     * Creates the options array
114     *
115     * @return array
116     */
117    protected function getOptions() {
118        $schema = $this->config['schema'];
119        $column = $this->getLookupColumn();
120        if(!$column) return array();
121        $field = $column->getLabel();
122
123        $search = new Search();
124        $search->addSchema($schema);
125        $search->addColumn($field);
126        $search->addSort($field);
127        $result = $search->execute();
128        $pids = $search->getPids();
129        $len = count($result);
130
131        /** @var Value[][] $result */
132        $options = array('' => '');
133        for($i = 0; $i < $len; $i++) {
134            $options[$pids[$i]] = $result[$i][0]->getDisplayValue();
135        }
136        return $options;
137    }
138
139    /**
140     * Render using linked field
141     *
142     * @param int|string $value
143     * @param \Doku_Renderer $R
144     * @param string $mode
145     * @return bool
146     */
147    public function renderValue($value, \Doku_Renderer $R, $mode) {
148        list(, $value) = json_decode($value);
149        $column = $this->getLookupColumn();
150        if(!$column) return false;
151        return $column->getType()->renderValue($value, $R, $mode);
152    }
153
154    /**
155     * Render using linked field
156     *
157     * @param \int[]|\string[] $values
158     * @param \Doku_Renderer $R
159     * @param string $mode
160     * @return bool
161     */
162    public function renderMultiValue($values, \Doku_Renderer $R, $mode) {
163        $values = array_map(
164            function ($val) {
165                list(, $val) = json_decode($val);
166                return $val;
167            }, $values
168        );
169        $column = $this->getLookupColumn();
170        if(!$column) return false;
171        return $column->getType()->renderMultiValue($values, $R, $mode);
172    }
173
174    /**
175     * @param string $value
176     * @return string
177     */
178    public function rawValue($value) {
179        list($value) = json_decode($value);
180        return $value;
181    }
182
183    /**
184     * @param string $value
185     * @return string
186     */
187    public function displayValue($value) {
188        list(, $value) = json_decode($value);
189        $column = $this->getLookupColumn();
190        if($column) {
191            return $column->getType()->displayValue($value);
192        } else {
193            return '';
194        }
195    }
196
197    /**
198     * This is the value to be used as argument to a filter for another column.
199     *
200     * In a sense this is the counterpart to the @see filter() function
201     *
202     * @param string $value
203     *
204     * @return string
205     */
206    public function compareValue($value) {
207        list(, $value) = json_decode($value);
208        $column = $this->getLookupColumn();
209        if($column) {
210            return $column->getType()->rawValue($value);
211        } else {
212            return '';
213        }
214    }
215
216
217    /**
218     * Merge with lookup table
219     *
220     * @param QueryBuilder $QB
221     * @param string $tablealias
222     * @param string $colname
223     * @param string $alias
224     */
225    public function select(QueryBuilder $QB, $tablealias, $colname, $alias) {
226        $schema = 'data_' . $this->config['schema'];
227        $column = $this->getLookupColumn();
228        if(!$column) {
229            parent::select($QB, $tablealias, $colname, $alias);
230            return;
231        }
232
233        $field = $column->getColName();
234        $rightalias = $QB->generateTableAlias();
235        $QB->addLeftJoin(
236            $tablealias, $schema, $rightalias,
237            "$tablealias.$colname = $rightalias.pid AND $rightalias.latest = 1"
238        );
239        $column->getType()->select($QB, $rightalias, $field, $alias);
240        $sql = $QB->getSelectStatement($alias);
241        $QB->addSelectStatement("STRUCT_JSON($tablealias.$colname, $sql)", $alias);
242    }
243
244    /**
245     * Compare against lookup table
246     *
247     * @param QueryBuilderWhere $add
248     * @param string $tablealias
249     * @param string $colname
250     * @param string $comp
251     * @param string|\string[] $value
252     * @param string $op
253     */
254    public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op) {
255        $schema = 'data_' . $this->config['schema'];
256        $column = $this->getLookupColumn();
257        if(!$column) {
258            parent::filter($add, $tablealias, $colname, $comp, $value, $op);
259            return;
260        }
261        $field = $column->getColName();
262
263        // compare against lookup field
264        $QB = $add->getQB();
265        $rightalias = $QB->generateTableAlias();
266        $QB->addLeftJoin(
267            $tablealias, $schema, $rightalias,
268            "$tablealias.$colname = $rightalias.pid AND $rightalias.latest = 1"
269        );
270        $column->getType()->filter($add, $rightalias, $field, $comp, $value, $op);
271    }
272
273    /**
274     * Sort by lookup table
275     *
276     * @param QueryBuilder $QB
277     * @param string $tablealias
278     * @param string $colname
279     * @param string $order
280     */
281    public function sort(QueryBuilder $QB, $tablealias, $colname, $order) {
282        $schema = 'data_' . $this->config['schema'];
283        $column = $this->getLookupColumn();
284        if(!$column) {
285            parent::sort($QB, $tablealias, $colname, $order);
286            return;
287        }
288        $field = $column->getColName();
289
290        $rightalias = $QB->generateTableAlias();
291        $QB->addLeftJoin(
292            $tablealias, $schema, $rightalias,
293            "$tablealias.$colname = $rightalias.pid AND $rightalias.latest = 1"
294        );
295        $column->getType()->sort($QB, $rightalias, $field, $order);
296    }
297
298}
299