xref: /plugin/struct/types/Lookup.php (revision 24eec657c05b0d72bf071b3cc0551de15e9d5f32)
19b8dc0b2SAndreas Gohr<?php
2d6d97f60SAnna Dabrowska
39b8dc0b2SAndreas Gohrnamespace dokuwiki\plugin\struct\types;
49b8dc0b2SAndreas Gohr
59b8dc0b2SAndreas Gohruse dokuwiki\plugin\struct\meta\Column;
60549dcc5SAndreas Gohruse dokuwiki\plugin\struct\meta\PageColumn;
79b8dc0b2SAndreas Gohruse dokuwiki\plugin\struct\meta\QueryBuilder;
8af993d55SMichael Grosseuse dokuwiki\plugin\struct\meta\QueryBuilderWhere;
90549dcc5SAndreas Gohruse dokuwiki\plugin\struct\meta\RevisionColumn;
100549dcc5SAndreas Gohruse dokuwiki\plugin\struct\meta\RowColumn;
119b8dc0b2SAndreas Gohruse dokuwiki\plugin\struct\meta\Schema;
129b8dc0b2SAndreas Gohruse dokuwiki\plugin\struct\meta\Search;
1388b58a21SSzymon Olewniczakuse dokuwiki\plugin\struct\meta\SummaryColumn;
1410b11cc5SSzymon Olewniczakuse dokuwiki\plugin\struct\meta\UserColumn;
150549dcc5SAndreas Gohruse dokuwiki\plugin\struct\meta\Value;
169b8dc0b2SAndreas Gohr
17d6d97f60SAnna Dabrowskaclass Lookup extends Dropdown
18d6d97f60SAnna Dabrowska{
19*24eec657SAndreas Gohr    protected $config = [
20*24eec657SAndreas Gohr        'schema' => '',
21*24eec657SAndreas Gohr        'field' => '',
22*24eec657SAndreas Gohr        'combobox' => false,
23*24eec657SAndreas Gohr    ];
249b8dc0b2SAndreas Gohr
25f256f53fSAndreas Gohr    /** @var  Column caches the referenced column */
267234bfb1Ssplitbrain    protected $column;
27f256f53fSAndreas Gohr
289b8dc0b2SAndreas Gohr    /**
299b8dc0b2SAndreas Gohr     * Dropdown constructor.
309b8dc0b2SAndreas Gohr     *
319b8dc0b2SAndreas Gohr     * @param array|null $config
329b8dc0b2SAndreas Gohr     * @param string $label
339b8dc0b2SAndreas Gohr     * @param bool $ismulti
349b8dc0b2SAndreas Gohr     * @param int $tid
359b8dc0b2SAndreas Gohr     */
36d6d97f60SAnna Dabrowska    public function __construct($config = null, $label = '', $ismulti = false, $tid = 0)
37d6d97f60SAnna Dabrowska    {
389b8dc0b2SAndreas Gohr        parent::__construct($config, $label, $ismulti, $tid);
399b8dc0b2SAndreas Gohr        $this->config['schema'] = Schema::cleanTableName($this->config['schema']);
409b8dc0b2SAndreas Gohr    }
419b8dc0b2SAndreas Gohr
429b8dc0b2SAndreas Gohr    /**
43dd6f0fd7SAndreas Gohr     * Get the configured loojup column
44dd6f0fd7SAndreas Gohr     *
459b8dc0b2SAndreas Gohr     * @return Column|false
469b8dc0b2SAndreas Gohr     */
47d6d97f60SAnna Dabrowska    protected function getLookupColumn()
48d6d97f60SAnna Dabrowska    {
497234bfb1Ssplitbrain        if ($this->column instanceof Column) return $this->column;
50dd6f0fd7SAndreas Gohr        $this->column = $this->getColumn($this->config['schema'], $this->config['field']);
51dd6f0fd7SAndreas Gohr        return $this->column;
52dd6f0fd7SAndreas Gohr    }
539b8dc0b2SAndreas Gohr
54dd6f0fd7SAndreas Gohr    /**
55dd6f0fd7SAndreas Gohr     * Gets the given column, applies language place holder
56dd6f0fd7SAndreas Gohr     *
57dd6f0fd7SAndreas Gohr     * @param string $table
58d329f04cSAndreas Gohr     * @param string $infield
59dd6f0fd7SAndreas Gohr     * @return Column|false
60dd6f0fd7SAndreas Gohr     */
61d6d97f60SAnna Dabrowska    protected function getColumn($table, $infield)
62d6d97f60SAnna Dabrowska    {
63dd6f0fd7SAndreas Gohr        global $conf;
64dd6f0fd7SAndreas Gohr
65dd6f0fd7SAndreas Gohr        $table = new Schema($table);
66dd6f0fd7SAndreas Gohr        if (!$table->getId()) {
679b8dc0b2SAndreas Gohr            // schema does not exist
68dd6f0fd7SAndreas Gohr            msg(sprintf('Schema %s does not exist', $table), -1);
699b8dc0b2SAndreas Gohr            return false;
709b8dc0b2SAndreas Gohr        }
719b8dc0b2SAndreas Gohr
729b8dc0b2SAndreas Gohr        // apply language replacement
73dd6f0fd7SAndreas Gohr        $field = str_replace('$LANG', $conf['lang'], $infield);
74dd6f0fd7SAndreas Gohr        $column = $table->findColumn($field);
759b8dc0b2SAndreas Gohr        if (!$column) {
76dd6f0fd7SAndreas Gohr            $field = str_replace('$LANG', 'en', $infield); // fallback to en
77dd6f0fd7SAndreas Gohr            $column = $table->findColumn($field);
789b8dc0b2SAndreas Gohr        }
799b8dc0b2SAndreas Gohr        if (!$column) {
8010b11cc5SSzymon Olewniczak            if ($infield == '%pageid%') {
8110b11cc5SSzymon Olewniczak                $column = new PageColumn(0, new Page(), $table);
8210b11cc5SSzymon Olewniczak            }
8310b11cc5SSzymon Olewniczak            if ($infield == '%title%') {
847234bfb1Ssplitbrain                $column = new PageColumn(0, new Page(['usetitles' => true]), $table);
8510b11cc5SSzymon Olewniczak            }
8610b11cc5SSzymon Olewniczak            if ($infield == '%lastupdate%') {
8710b11cc5SSzymon Olewniczak                $column = new RevisionColumn(0, new DateTime(), $table);
8810b11cc5SSzymon Olewniczak            }
8910b11cc5SSzymon Olewniczak            if ($infield == '%lasteditor%') {
9010b11cc5SSzymon Olewniczak                $column = new UserColumn(0, new User(), $table);
9110b11cc5SSzymon Olewniczak            }
9288b58a21SSzymon Olewniczak            if ($infield == '%lastsummary%') {
936781c68dSSzymon Olewniczak                return new SummaryColumn(0, new AutoSummary(), $table);
9488b58a21SSzymon Olewniczak            }
9510b11cc5SSzymon Olewniczak            if ($infield == '%rowid%') {
9610b11cc5SSzymon Olewniczak                $column = new RowColumn(0, new Decimal(), $table);
9710b11cc5SSzymon Olewniczak            }
9810b11cc5SSzymon Olewniczak        }
9910b11cc5SSzymon Olewniczak        if (!$column) {
1009b8dc0b2SAndreas Gohr            // field does not exist
101dd6f0fd7SAndreas Gohr            msg(sprintf('Field %s.%s does not exist', $table, $infield), -1);
1029b8dc0b2SAndreas Gohr            return false;
1039b8dc0b2SAndreas Gohr        }
1049b8dc0b2SAndreas Gohr
1059b8dc0b2SAndreas Gohr        if ($column->isMulti()) {
1069b8dc0b2SAndreas Gohr            // field is multi
107dd6f0fd7SAndreas Gohr            msg(sprintf('Field %s.%s is a multi field - not allowed for lookup', $table, $field), -1);
1089b8dc0b2SAndreas Gohr            return false;
1099b8dc0b2SAndreas Gohr        }
1109b8dc0b2SAndreas Gohr
1119b8dc0b2SAndreas Gohr        return $column;
1129b8dc0b2SAndreas Gohr    }
1139b8dc0b2SAndreas Gohr
1149b8dc0b2SAndreas Gohr    /**
1159b8dc0b2SAndreas Gohr     * Creates the options array
1169b8dc0b2SAndreas Gohr     *
1179b8dc0b2SAndreas Gohr     * @return array
1189b8dc0b2SAndreas Gohr     */
119d6d97f60SAnna Dabrowska    protected function getOptions()
120d6d97f60SAnna Dabrowska    {
1219b8dc0b2SAndreas Gohr        $schema = $this->config['schema'];
1229b8dc0b2SAndreas Gohr        $column = $this->getLookupColumn();
1237234bfb1Ssplitbrain        if (!$column) return [];
1249b8dc0b2SAndreas Gohr        $field = $column->getLabel();
1259b8dc0b2SAndreas Gohr
1269b8dc0b2SAndreas Gohr        $search = new Search();
1279b8dc0b2SAndreas Gohr        $search->addSchema($schema);
1289b8dc0b2SAndreas Gohr        $search->addColumn($field);
1299b8dc0b2SAndreas Gohr        $search->addSort($field);
1307234bfb1Ssplitbrain
131ba7f5789SAnna Dabrowska        $result = $search->getRows();
132fbbae12aSAnna Dabrowska        $pids = $search->getPids();
1337f803aa8SAnna Dabrowska        $rids = $search->getRids();
1349b8dc0b2SAndreas Gohr        $len = count($result);
1359b8dc0b2SAndreas Gohr
1367234bfb1Ssplitbrain        $options = ['' => ''];
1379b8dc0b2SAndreas Gohr        for ($i = 0; $i < $len; $i++) {
1385e29103aSannda            $val = json_encode([$pids[$i], (int)$rids[$i]], JSON_THROW_ON_ERROR);
139fbbae12aSAnna Dabrowska            $options[$val] = $result[$i][0]->getDisplayValue();
1409b8dc0b2SAndreas Gohr        }
1419b8dc0b2SAndreas Gohr        return $options;
1429b8dc0b2SAndreas Gohr    }
1439b8dc0b2SAndreas Gohr
144fbbae12aSAnna Dabrowska
1459b8dc0b2SAndreas Gohr    /**
1469b8dc0b2SAndreas Gohr     * Render using linked field
1479b8dc0b2SAndreas Gohr     *
1489b8dc0b2SAndreas Gohr     * @param int|string $value
1499b8dc0b2SAndreas Gohr     * @param \Doku_Renderer $R
1509b8dc0b2SAndreas Gohr     * @param string $mode
1519b8dc0b2SAndreas Gohr     * @return bool
1529b8dc0b2SAndreas Gohr     */
153d6d97f60SAnna Dabrowska    public function renderValue($value, \Doku_Renderer $R, $mode)
154d6d97f60SAnna Dabrowska    {
1557234bfb1Ssplitbrain        [, $value] = \helper_plugin_struct::decodeJson($value);
1569b8dc0b2SAndreas Gohr        $column = $this->getLookupColumn();
1579b8dc0b2SAndreas Gohr        if (!$column) return false;
1589b8dc0b2SAndreas Gohr        return $column->getType()->renderValue($value, $R, $mode);
1599b8dc0b2SAndreas Gohr    }
1609b8dc0b2SAndreas Gohr
1619b8dc0b2SAndreas Gohr    /**
1629b8dc0b2SAndreas Gohr     * Render using linked field
1639b8dc0b2SAndreas Gohr     *
1649b8dc0b2SAndreas Gohr     * @param \int[]|\string[] $values
1659b8dc0b2SAndreas Gohr     * @param \Doku_Renderer $R
1669b8dc0b2SAndreas Gohr     * @param string $mode
1679b8dc0b2SAndreas Gohr     * @return bool
1689b8dc0b2SAndreas Gohr     */
169d6d97f60SAnna Dabrowska    public function renderMultiValue($values, \Doku_Renderer $R, $mode)
170d6d97f60SAnna Dabrowska    {
1719b8dc0b2SAndreas Gohr        $values = array_map(
1729b8dc0b2SAndreas Gohr            function ($val) {
1737234bfb1Ssplitbrain                [, $val] = \helper_plugin_struct::decodeJson($val);
1749b8dc0b2SAndreas Gohr                return $val;
175d6d97f60SAnna Dabrowska            },
176d6d97f60SAnna Dabrowska            $values
1779b8dc0b2SAndreas Gohr        );
1789b8dc0b2SAndreas Gohr        $column = $this->getLookupColumn();
1799b8dc0b2SAndreas Gohr        if (!$column) return false;
1809b8dc0b2SAndreas Gohr        return $column->getType()->renderMultiValue($values, $R, $mode);
1819b8dc0b2SAndreas Gohr    }
1829b8dc0b2SAndreas Gohr
1839b8dc0b2SAndreas Gohr    /**
1849b8dc0b2SAndreas Gohr     * @param string $value
1859b8dc0b2SAndreas Gohr     * @return string
1869b8dc0b2SAndreas Gohr     */
187d6d97f60SAnna Dabrowska    public function rawValue($value)
188d6d97f60SAnna Dabrowska    {
1897234bfb1Ssplitbrain        [$value] = \helper_plugin_struct::decodeJson($value);
1909b8dc0b2SAndreas Gohr        return $value;
1919b8dc0b2SAndreas Gohr    }
1929b8dc0b2SAndreas Gohr
1939b8dc0b2SAndreas Gohr    /**
1949b8dc0b2SAndreas Gohr     * @param string $value
1959b8dc0b2SAndreas Gohr     * @return string
1969b8dc0b2SAndreas Gohr     */
197d6d97f60SAnna Dabrowska    public function displayValue($value)
198d6d97f60SAnna Dabrowska    {
1997234bfb1Ssplitbrain        [, $value] = \helper_plugin_struct::decodeJson($value);
2009b8dc0b2SAndreas Gohr        $column = $this->getLookupColumn();
2019b8dc0b2SAndreas Gohr        if ($column) {
2029b8dc0b2SAndreas Gohr            return $column->getType()->displayValue($value);
2039b8dc0b2SAndreas Gohr        } else {
2049b8dc0b2SAndreas Gohr            return '';
2059b8dc0b2SAndreas Gohr        }
2069b8dc0b2SAndreas Gohr    }
2079b8dc0b2SAndreas Gohr
2089b8dc0b2SAndreas Gohr    /**
2097717c082SMichael Große     * This is the value to be used as argument to a filter for another column.
2107717c082SMichael Große     *
2110549dcc5SAndreas Gohr     * In a sense this is the counterpart to the @param string $value
2127717c082SMichael Große     *
2137717c082SMichael Große     * @return string
2140549dcc5SAndreas Gohr     * @see filter() function
2150549dcc5SAndreas Gohr     *
2167717c082SMichael Große     */
217d6d97f60SAnna Dabrowska    public function compareValue($value)
218d6d97f60SAnna Dabrowska    {
2197234bfb1Ssplitbrain        [, $value] = \helper_plugin_struct::decodeJson($value);
2207717c082SMichael Große        $column = $this->getLookupColumn();
2217717c082SMichael Große        if ($column) {
2227717c082SMichael Große            return $column->getType()->rawValue($value);
2237717c082SMichael Große        } else {
2247717c082SMichael Große            return '';
2257717c082SMichael Große        }
2267717c082SMichael Große    }
2277717c082SMichael Große
2287717c082SMichael Große
2297717c082SMichael Große    /**
2309b8dc0b2SAndreas Gohr     * Merge with lookup table
2319b8dc0b2SAndreas Gohr     *
2329b8dc0b2SAndreas Gohr     * @param QueryBuilder $QB
2339b8dc0b2SAndreas Gohr     * @param string $tablealias
2349b8dc0b2SAndreas Gohr     * @param string $colname
2359b8dc0b2SAndreas Gohr     * @param string $alias
2369b8dc0b2SAndreas Gohr     */
237d6d97f60SAnna Dabrowska    public function select(QueryBuilder $QB, $tablealias, $colname, $alias)
238d6d97f60SAnna Dabrowska    {
2399b8dc0b2SAndreas Gohr        $schema = 'data_' . $this->config['schema'];
2409b8dc0b2SAndreas Gohr        $column = $this->getLookupColumn();
2419b8dc0b2SAndreas Gohr        if (!$column) {
2429b8dc0b2SAndreas Gohr            parent::select($QB, $tablealias, $colname, $alias);
2439b8dc0b2SAndreas Gohr            return;
2449b8dc0b2SAndreas Gohr        }
2459b8dc0b2SAndreas Gohr
2469b8dc0b2SAndreas Gohr        $field = $column->getColName();
2479b8dc0b2SAndreas Gohr        $rightalias = $QB->generateTableAlias();
2489b8dc0b2SAndreas Gohr        $QB->addLeftJoin(
249d6d97f60SAnna Dabrowska            $tablealias,
250d6d97f60SAnna Dabrowska            $schema,
251d6d97f60SAnna Dabrowska            $rightalias,
252cdd16494SAnna Dabrowska            "STRUCT_LOOKUP($tablealias.$colname, 0) = $rightalias.pid " .
253cdd16494SAnna Dabrowska            "AND STRUCT_LOOKUP($tablealias.$colname, 1) = $rightalias.rid " .
25417a3a578SAndreas Gohr            "AND $rightalias.latest = 1"
2559b8dc0b2SAndreas Gohr        );
2569b8dc0b2SAndreas Gohr        $column->getType()->select($QB, $rightalias, $field, $alias);
2579b8dc0b2SAndreas Gohr        $sql = $QB->getSelectStatement($alias);
2589b8dc0b2SAndreas Gohr        $QB->addSelectStatement("STRUCT_JSON($tablealias.$colname, $sql)", $alias);
2599b8dc0b2SAndreas Gohr    }
2609b8dc0b2SAndreas Gohr
2619b8dc0b2SAndreas Gohr    /**
2629b8dc0b2SAndreas Gohr     * Compare against lookup table
2639b8dc0b2SAndreas Gohr     *
264af993d55SMichael Grosse     * @param QueryBuilderWhere $add
2659b8dc0b2SAndreas Gohr     * @param string $tablealias
2669b8dc0b2SAndreas Gohr     * @param string $colname
2679b8dc0b2SAndreas Gohr     * @param string $comp
2689b8dc0b2SAndreas Gohr     * @param string|\string[] $value
2699b8dc0b2SAndreas Gohr     * @param string $op
2709b8dc0b2SAndreas Gohr     */
271d6d97f60SAnna Dabrowska    public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op)
272d6d97f60SAnna Dabrowska    {
2739b8dc0b2SAndreas Gohr        $schema = 'data_' . $this->config['schema'];
2749b8dc0b2SAndreas Gohr        $column = $this->getLookupColumn();
2759b8dc0b2SAndreas Gohr        if (!$column) {
276af993d55SMichael Grosse            parent::filter($add, $tablealias, $colname, $comp, $value, $op);
2779b8dc0b2SAndreas Gohr            return;
2789b8dc0b2SAndreas Gohr        }
2799b8dc0b2SAndreas Gohr        $field = $column->getColName();
2809b8dc0b2SAndreas Gohr
2819b8dc0b2SAndreas Gohr        // compare against lookup field
282af993d55SMichael Grosse        $QB = $add->getQB();
2839b8dc0b2SAndreas Gohr        $rightalias = $QB->generateTableAlias();
2849b8dc0b2SAndreas Gohr        $QB->addLeftJoin(
285d6d97f60SAnna Dabrowska            $tablealias,
286d6d97f60SAnna Dabrowska            $schema,
287d6d97f60SAnna Dabrowska            $rightalias,
28817a3a578SAndreas Gohr            "$tablealias.$colname = STRUCT_JSON($rightalias.pid, CAST($rightalias.rid AS DECIMAL)) AND " .
28917a3a578SAndreas Gohr            "$rightalias.latest = 1"
2909b8dc0b2SAndreas Gohr        );
291af993d55SMichael Grosse        $column->getType()->filter($add, $rightalias, $field, $comp, $value, $op);
2929b8dc0b2SAndreas Gohr    }
2939b8dc0b2SAndreas Gohr
2949b8dc0b2SAndreas Gohr    /**
2959b8dc0b2SAndreas Gohr     * Sort by lookup table
2969b8dc0b2SAndreas Gohr     *
2979b8dc0b2SAndreas Gohr     * @param QueryBuilder $QB
2989b8dc0b2SAndreas Gohr     * @param string $tablealias
2999b8dc0b2SAndreas Gohr     * @param string $colname
3009b8dc0b2SAndreas Gohr     * @param string $order
3019b8dc0b2SAndreas Gohr     */
302d6d97f60SAnna Dabrowska    public function sort(QueryBuilder $QB, $tablealias, $colname, $order)
303d6d97f60SAnna Dabrowska    {
3049b8dc0b2SAndreas Gohr        $schema = 'data_' . $this->config['schema'];
3059b8dc0b2SAndreas Gohr        $column = $this->getLookupColumn();
3069b8dc0b2SAndreas Gohr        if (!$column) {
3079b8dc0b2SAndreas Gohr            parent::sort($QB, $tablealias, $colname, $order);
3089b8dc0b2SAndreas Gohr            return;
3099b8dc0b2SAndreas Gohr        }
3109b8dc0b2SAndreas Gohr        $field = $column->getColName();
3119b8dc0b2SAndreas Gohr
3129b8dc0b2SAndreas Gohr        $rightalias = $QB->generateTableAlias();
3139b8dc0b2SAndreas Gohr        $QB->addLeftJoin(
314d6d97f60SAnna Dabrowska            $tablealias,
315d6d97f60SAnna Dabrowska            $schema,
316d6d97f60SAnna Dabrowska            $rightalias,
31717a3a578SAndreas Gohr            "$tablealias.$colname = STRUCT_JSON($rightalias.pid, CAST($rightalias.rid AS DECIMAL)) AND " .
31817a3a578SAndreas Gohr            "$rightalias.latest = 1"
3199b8dc0b2SAndreas Gohr        );
3209b8dc0b2SAndreas Gohr        $column->getType()->sort($QB, $rightalias, $field, $order);
3219b8dc0b2SAndreas Gohr    }
3229b8dc0b2SAndreas Gohr}
323