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