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