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