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