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