1<?php
2namespace dokuwiki\plugin\structjoin\types;
3
4use dokuwiki\plugin\struct\meta\QueryBuilder;
5use dokuwiki\plugin\struct\meta\QueryBuilderWhere;
6use dokuwiki\plugin\struct\meta\StructException;
7use dokuwiki\plugin\struct\types\AbstractBaseType;
8use dokuwiki\plugin\struct\types\Lookup;
9use ReflectionClass;
10
11class Join extends Lookup {
12
13    public function valueEditor($name, $rawvalue, $htmlID)
14    {
15        return "<strong>{$this->config['schema']}.{$this->config['field']}</strong>";
16    }
17
18    /**
19     * Render using linked field
20     *
21     * @param int|string $value
22     * @param \Doku_Renderer $R
23     * @param string $mode
24     * @return bool
25     */
26    public function renderValue($value, \Doku_Renderer $R, $mode) {
27        $column = $this->getLookupColumn();
28        if(!$column) return false;
29        return $column->getType()->renderValue($value, $R, $mode);
30    }
31
32    /**
33     * Render using linked field
34     *
35     * @param \int[]|\string[] $values
36     * @param \Doku_Renderer $R
37     * @param string $mode
38     * @return bool
39     */
40    public function renderMultiValue($values, \Doku_Renderer $R, $mode) {
41        $column = $this->getLookupColumn();
42        if(!$column) return false;
43        return $column->getType()->renderMultiValue($values, $R, $mode);
44    }
45
46    /**
47     * @param string $value
48     * @return string
49     */
50    public function rawValue($value) {
51        return $value;
52    }
53
54    /**
55     * @param string $value
56     * @return string
57     */
58    public function displayValue($value) {
59        $column = $this->getLookupColumn();
60        if($column) {
61            return $column->getType()->displayValue($value);
62        } else {
63            return '';
64        }
65    }
66
67    /**
68     * This is the value to be used as argument to a filter for another column.
69     *
70     * In a sense this is the counterpart to the @see filter() function
71     *
72     * @param string $value
73     *
74     * @return string
75     */
76    public function compareValue($value) {
77        $column = $this->getLookupColumn();
78        if($column) {
79            return $column->getType()->rawValue($value);
80        } else {
81            return '';
82        }
83    }
84
85    /**
86     * Merge with lookup table
87     *
88     * @param QueryBuilder $QB
89     * @param string $tablealias
90     * @param string $colname
91     * @param string $alias
92     * @throws \ReflectionException
93     */
94    public function select(QueryBuilder $QB, $tablealias, $colname, $alias) {
95        $column = $this->getLookupColumn();
96        if(!$column) {
97            AbstractBaseType::select($QB, $tablealias, $colname, $alias);
98            return;
99        }
100
101        $rightalias = $this->findRightAlias($QB);
102        if (!$rightalias) {
103            throw new StructException("Select Lookup column to use Join.");
104        }
105
106        $field = $column->getColName();
107        $column->getType()->select($QB, $rightalias, $field, $alias);
108    }
109
110    /**
111     * @param QueryBuilder $QB
112     * @return false|int|string
113     * @throws \ReflectionException
114     */
115    protected function findRightAlias(QueryBuilder $QB) {
116        $from = $this->getProtectedPropertyFromQB($QB, 'from');
117        $leftJoinAliases = array_filter(array_map(function($from) {
118            if (preg_match('/^LEFT OUTER JOIN data_([^\s]*)/', $from, $matches)) {
119                return $matches[1];
120            }
121            return false;
122        }, $from));
123
124        return array_search($this->config['schema'], $leftJoinAliases);
125    }
126
127    /**
128     * @param QueryBuilder $QB
129     * @param $property
130     * @return mixed
131     * @throws \ReflectionException
132     */
133    protected function getProtectedPropertyFromQB(QueryBuilder $QB, $property) {
134        $reflectionClass = new ReflectionClass(QueryBuilder::class);
135        $reflectionProperty = $reflectionClass->getProperty($property);
136        $reflectionProperty->setAccessible(true);
137
138        return $reflectionProperty->getValue($QB);
139    }
140
141    /**
142     * Compare against lookup table
143     *
144     * @param QueryBuilderWhere $add
145     * @param string $tablealias
146     * @param string $colname
147     * @param string $comp
148     * @param string|\string[] $value
149     * @param string $op
150     */
151    public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op) {
152        $schema = 'data_' . $this->config['schema'];
153        $column = $this->getLookupColumn();
154        if(!$column) {
155            AbstractBaseType::filter($add, $tablealias, $colname, $comp, $value, $op);
156            return;
157        }
158        $field = $column->getColName();
159
160        // compare against lookup field
161        $QB = $add->getQB();
162        $rightalias = $this->findRightAlias($QB);
163        if (!$rightalias) {
164            throw new StructException("Select Lookup column to use Join.");
165        }
166        $column->getType()->filter($add, $rightalias, $field, $comp, $value, $op);
167    }
168
169    /**
170     * Sort by lookup table
171     *
172     * @param QueryBuilder $QB
173     * @param string $tablealias
174     * @param string $colname
175     * @param string $order
176     * @throws \ReflectionException
177     */
178    public function sort(QueryBuilder $QB, $tablealias, $colname, $order) {
179        $schema = 'data_' . $this->config['schema'];
180        $column = $this->getLookupColumn();
181        if(!$column) {
182            AbstractBaseType::sort($QB, $tablealias, $colname, $order);
183            return;
184        }
185        $field = $column->getColName();
186
187        $rightalias = $this->findRightAlias($QB);
188        if (!$rightalias) {
189            throw new StructException("Select Lookup column to use Join.");
190        }
191        $column->getType()->sort($QB, $rightalias, $field, $order);
192    }
193}
194