1<?php 2 3namespace dokuwiki\plugin\struct\types; 4 5use dokuwiki\plugin\struct\meta\Column; 6use dokuwiki\plugin\struct\meta\PageColumn; 7use dokuwiki\plugin\struct\meta\QueryBuilder; 8use dokuwiki\plugin\struct\meta\QueryBuilderWhere; 9use dokuwiki\plugin\struct\meta\RevisionColumn; 10use dokuwiki\plugin\struct\meta\RowColumn; 11use dokuwiki\plugin\struct\meta\Schema; 12use dokuwiki\plugin\struct\meta\Search; 13use dokuwiki\plugin\struct\meta\SummaryColumn; 14use dokuwiki\plugin\struct\meta\UserColumn; 15use dokuwiki\plugin\struct\meta\Value; 16 17class Lookup extends Dropdown 18{ 19 protected $config = ['schema' => '', 'field' => '']; 20 21 /** @var Column caches the referenced column */ 22 protected $column; 23 24 /** 25 * Dropdown constructor. 26 * 27 * @param array|null $config 28 * @param string $label 29 * @param bool $ismulti 30 * @param int $tid 31 */ 32 public function __construct($config = null, $label = '', $ismulti = false, $tid = 0) 33 { 34 parent::__construct($config, $label, $ismulti, $tid); 35 $this->config['schema'] = Schema::cleanTableName($this->config['schema']); 36 } 37 38 /** 39 * Get the configured loojup column 40 * 41 * @return Column|false 42 */ 43 protected function getLookupColumn() 44 { 45 if ($this->column instanceof Column) return $this->column; 46 $this->column = $this->getColumn($this->config['schema'], $this->config['field']); 47 return $this->column; 48 } 49 50 /** 51 * Gets the given column, applies language place holder 52 * 53 * @param string $table 54 * @param string $infield 55 * @return Column|false 56 */ 57 protected function getColumn($table, $infield) 58 { 59 global $conf; 60 61 $table = new Schema($table); 62 if (!$table->getId()) { 63 // schema does not exist 64 msg(sprintf('Schema %s does not exist', $table), -1); 65 return false; 66 } 67 68 // apply language replacement 69 $field = str_replace('$LANG', $conf['lang'], $infield); 70 $column = $table->findColumn($field); 71 if (!$column) { 72 $field = str_replace('$LANG', 'en', $infield); // fallback to en 73 $column = $table->findColumn($field); 74 } 75 if (!$column) { 76 if ($infield == '%pageid%') { 77 $column = new PageColumn(0, new Page(), $table); 78 } 79 if ($infield == '%title%') { 80 $column = new PageColumn(0, new Page(['usetitles' => true]), $table); 81 } 82 if ($infield == '%lastupdate%') { 83 $column = new RevisionColumn(0, new DateTime(), $table); 84 } 85 if ($infield == '%lasteditor%') { 86 $column = new UserColumn(0, new User(), $table); 87 } 88 if ($infield == '%lastsummary%') { 89 return new SummaryColumn(0, new AutoSummary(), $table); 90 } 91 if ($infield == '%rowid%') { 92 $column = new RowColumn(0, new Decimal(), $table); 93 } 94 } 95 if (!$column) { 96 // field does not exist 97 msg(sprintf('Field %s.%s does not exist', $table, $infield), -1); 98 return false; 99 } 100 101 if ($column->isMulti()) { 102 // field is multi 103 msg(sprintf('Field %s.%s is a multi field - not allowed for lookup', $table, $field), -1); 104 return false; 105 } 106 107 return $column; 108 } 109 110 /** 111 * Creates the options array 112 * 113 * @return array 114 */ 115 protected function getOptions() 116 { 117 $schema = $this->config['schema']; 118 $column = $this->getLookupColumn(); 119 if (!$column) return []; 120 $field = $column->getLabel(); 121 122 $search = new Search(); 123 $search->addSchema($schema); 124 $search->addColumn($field); 125 $search->addSort($field); 126 127 $result = $search->getRows(); 128 $pids = $search->getPids(); 129 $rids = $search->getRids(); 130 $len = count($result); 131 132 $options = ['' => '']; 133 for ($i = 0; $i < $len; $i++) { 134 $val = json_encode([$pids[$i], (int)$rids[$i]], JSON_THROW_ON_ERROR); 135 $options[$val] = $result[$i][0]->getDisplayValue(); 136 } 137 return $options; 138 } 139 140 141 /** 142 * Render using linked field 143 * 144 * @param int|string $value 145 * @param \Doku_Renderer $R 146 * @param string $mode 147 * @return bool 148 */ 149 public function renderValue($value, \Doku_Renderer $R, $mode) 150 { 151 [, $value] = \helper_plugin_struct::decodeJson($value); 152 $column = $this->getLookupColumn(); 153 if (!$column) return false; 154 return $column->getType()->renderValue($value, $R, $mode); 155 } 156 157 /** 158 * Render using linked field 159 * 160 * @param \int[]|\string[] $values 161 * @param \Doku_Renderer $R 162 * @param string $mode 163 * @return bool 164 */ 165 public function renderMultiValue($values, \Doku_Renderer $R, $mode) 166 { 167 $values = array_map( 168 function ($val) { 169 [, $val] = \helper_plugin_struct::decodeJson($val); 170 return $val; 171 }, 172 $values 173 ); 174 $column = $this->getLookupColumn(); 175 if (!$column) return false; 176 return $column->getType()->renderMultiValue($values, $R, $mode); 177 } 178 179 /** 180 * @param string $value 181 * @return string 182 */ 183 public function rawValue($value) 184 { 185 [$value] = \helper_plugin_struct::decodeJson($value); 186 return $value; 187 } 188 189 /** 190 * @param string $value 191 * @return string 192 */ 193 public function displayValue($value) 194 { 195 [, $value] = \helper_plugin_struct::decodeJson($value); 196 $column = $this->getLookupColumn(); 197 if ($column) { 198 return $column->getType()->displayValue($value); 199 } else { 200 return ''; 201 } 202 } 203 204 /** 205 * This is the value to be used as argument to a filter for another column. 206 * 207 * In a sense this is the counterpart to the @param string $value 208 * 209 * @return string 210 * @see filter() function 211 * 212 */ 213 public function compareValue($value) 214 { 215 [, $value] = \helper_plugin_struct::decodeJson($value); 216 $column = $this->getLookupColumn(); 217 if ($column) { 218 return $column->getType()->rawValue($value); 219 } else { 220 return ''; 221 } 222 } 223 224 225 /** 226 * Merge with lookup table 227 * 228 * @param QueryBuilder $QB 229 * @param string $tablealias 230 * @param string $colname 231 * @param string $alias 232 */ 233 public function select(QueryBuilder $QB, $tablealias, $colname, $alias) 234 { 235 $schema = 'data_' . $this->config['schema']; 236 $column = $this->getLookupColumn(); 237 if (!$column) { 238 parent::select($QB, $tablealias, $colname, $alias); 239 return; 240 } 241 242 $field = $column->getColName(); 243 $rightalias = $QB->generateTableAlias(); 244 $QB->addLeftJoin( 245 $tablealias, 246 $schema, 247 $rightalias, 248 "STRUCT_LOOKUP($tablealias.$colname, 0) = $rightalias.pid " . 249 "AND STRUCT_LOOKUP($tablealias.$colname, 1) = $rightalias.rid " . 250 "AND $rightalias.latest = 1" 251 ); 252 $column->getType()->select($QB, $rightalias, $field, $alias); 253 $sql = $QB->getSelectStatement($alias); 254 $QB->addSelectStatement("STRUCT_JSON($tablealias.$colname, $sql)", $alias); 255 } 256 257 /** 258 * Compare against lookup table 259 * 260 * @param QueryBuilderWhere $add 261 * @param string $tablealias 262 * @param string $colname 263 * @param string $comp 264 * @param string|\string[] $value 265 * @param string $op 266 */ 267 public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op) 268 { 269 $schema = 'data_' . $this->config['schema']; 270 $column = $this->getLookupColumn(); 271 if (!$column) { 272 parent::filter($add, $tablealias, $colname, $comp, $value, $op); 273 return; 274 } 275 $field = $column->getColName(); 276 277 // compare against lookup field 278 $QB = $add->getQB(); 279 $rightalias = $QB->generateTableAlias(); 280 $QB->addLeftJoin( 281 $tablealias, 282 $schema, 283 $rightalias, 284 "$tablealias.$colname = STRUCT_JSON($rightalias.pid, CAST($rightalias.rid AS DECIMAL)) AND " . 285 "$rightalias.latest = 1" 286 ); 287 $column->getType()->filter($add, $rightalias, $field, $comp, $value, $op); 288 } 289 290 /** 291 * Sort by lookup table 292 * 293 * @param QueryBuilder $QB 294 * @param string $tablealias 295 * @param string $colname 296 * @param string $order 297 */ 298 public function sort(QueryBuilder $QB, $tablealias, $colname, $order) 299 { 300 $schema = 'data_' . $this->config['schema']; 301 $column = $this->getLookupColumn(); 302 if (!$column) { 303 parent::sort($QB, $tablealias, $colname, $order); 304 return; 305 } 306 $field = $column->getColName(); 307 308 $rightalias = $QB->generateTableAlias(); 309 $QB->addLeftJoin( 310 $tablealias, 311 $schema, 312 $rightalias, 313 "$tablealias.$colname = STRUCT_JSON($rightalias.pid, CAST($rightalias.rid AS DECIMAL)) AND " . 314 "$rightalias.latest = 1" 315 ); 316 $column->getType()->sort($QB, $rightalias, $field, $order); 317 } 318} 319