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