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