xref: /plugin/struct/types/Lookup.php (revision 127d6bacbaa5831b9606d5ae91b03cfbe9108473)
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     * Merge with lookup table
171     *
172     * @param QueryBuilder $QB
173     * @param string $tablealias
174     * @param string $colname
175     * @param string $alias
176     */
177    public function select(QueryBuilder $QB, $tablealias, $colname, $alias) {
178        $schema = 'data_' . $this->config['schema'];
179        $column = $this->getLookupColumn();
180        if(!$column) {
181            parent::select($QB, $tablealias, $colname, $alias);
182            return;
183        }
184
185        $field = $column->getColName();
186        $rightalias = $QB->generateTableAlias();
187        $QB->addLeftJoin(
188            $tablealias, $schema, $rightalias,
189            "$tablealias.$colname = $rightalias.pid AND $rightalias.latest = 1"
190        );
191        $column->getType()->select($QB, $rightalias, $field, $alias);
192        $sql = $QB->getSelectStatement($alias);
193        $QB->addSelectStatement("STRUCT_JSON($tablealias.$colname, $sql)", $alias);
194    }
195
196    /**
197     * Compare against lookup table
198     *
199     * @param QueryBuilderWhere $add
200     * @param string $tablealias
201     * @param string $colname
202     * @param string $comp
203     * @param string|\string[] $value
204     * @param string $op
205     */
206    public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op) {
207        $schema = 'data_' . $this->config['schema'];
208        $column = $this->getLookupColumn();
209        if(!$column) {
210            parent::filter($add, $tablealias, $colname, $comp, $value, $op);
211            return;
212        }
213        $field = $column->getColName();
214
215        // compare against lookup field
216        $QB = $add->getQB();
217        $rightalias = $QB->generateTableAlias();
218        $QB->addLeftJoin(
219            $tablealias, $schema, $rightalias,
220            "$tablealias.$colname = $rightalias.pid AND $rightalias.latest = 1"
221        );
222        $column->getType()->filter($add, $rightalias, $field, $comp, $value, $op);
223    }
224
225    /**
226     * Sort by lookup table
227     *
228     * @param QueryBuilder $QB
229     * @param string $tablealias
230     * @param string $colname
231     * @param string $order
232     */
233    public function sort(QueryBuilder $QB, $tablealias, $colname, $order) {
234        $schema = 'data_' . $this->config['schema'];
235        $column = $this->getLookupColumn();
236        if(!$column) {
237            parent::sort($QB, $tablealias, $colname, $order);
238            return;
239        }
240        $field = $column->getColName();
241
242        $rightalias = $QB->generateTableAlias();
243        $QB->addLeftJoin(
244            $tablealias, $schema, $rightalias,
245            "$tablealias.$colname = $rightalias.pid AND $rightalias.latest = 1"
246        );
247        $column->getType()->sort($QB, $rightalias, $field, $order);
248    }
249
250}
251