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