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