1<?php 2 3namespace plugin\struct\meta; 4 5use Exception; 6 7class Search { 8 9 /** @var Schema[] list of schemas to query */ 10 protected $schemas = array(); 11 12 /** @var Column[] list of columns to select */ 13 protected $columns = array(); 14 15 /** @var array the sorting of the result */ 16 protected $sortby = array(); 17 18 /** @var array the or filters */ 19 protected $filteror = array(); 20 21 /** @var array the and filters */ 22 protected $filterand = array(); 23 24 /** @var array list of aliases tables can be referenced by */ 25 protected $aliases = array(); 26 27 /** 28 * Add a schema to be searched 29 * 30 * Call multiple times for multiple schemas. 31 * 32 * @param string $table 33 * @param string $alias 34 */ 35 public function addSchema($table, $alias = '') { 36 $this->schemas[$table] = new Schema($table); 37 if($alias) $this->aliases[$alias] = $table; 38 } 39 40 /** 41 * Add a column to be returned by the search 42 * 43 * Call multiple times for multiple columns. Be sure the referenced tables have been 44 * added before 45 * 46 * @param string $colname may contain an alias 47 */ 48 public function addColumn($colname) { 49 $col = $this->findColumn($colname); 50 if(!$col) return; //FIXME do we really want to ignore missing columns? 51 $this->columns[] = $col; 52 } 53 54 /** 55 * Add sorting options 56 * 57 * Call multiple times for multiple columns. Be sure the referenced tables have been 58 * added before 59 * 60 * @param string $colname may contain an alias 61 * @param bool $asc sort direction (ASC = true, DESC = false) 62 */ 63 public function addSort($colname, $asc = true) { 64 $col = $this->findColumn($colname); 65 if(!$col) return; //FIXME do we really want to ignore missing columns? 66 67 $this->sortby[] = array($col, $asc); 68 } 69 70 /** 71 * Adds an ORed filter 72 * 73 * @param string $colname may contain an alias 74 * @param string $value 75 * @param string $comp 76 */ 77 public function addFilterOr($colname, $value, $comp) { 78 $col = $this->findColumn($colname); 79 if(!$col) return; //FIXME do we really want to ignore missing columns? 80 81 $this->filteror[] = array($col, $value, $comp); 82 } 83 84 /** 85 * Adds an ANDed filter 86 * 87 * @param string $colname may contain an alias 88 * @param string $value 89 * @param string $comp 90 */ 91 public function addFilterAnd($colname, $value, $comp) { 92 $col = $this->findColumn($colname); 93 if(!$col) return; //FIXME do we really want to ignore missing columns? 94 95 $this->filterand[] = array($col, $value, $comp); 96 } 97 98 /** 99 * Transform the set search parameters into a statement 100 * 101 * @todo limit to the newest data! 102 * @return string 103 */ 104 public function getSQL() { 105 if(!$this->columns) throw new SearchException('nocolname'); 106 107 // basic tables 108 $from = ''; 109 foreach($this->schemas as $schema) { 110 $from .= 'data_' . $schema->getTable() . ', '; 111 112 // fixme join the multiple tables together by pid 113 } 114 $from = rtrim($from, ', '); 115 116 // columns to select, handling multis 117 $select = ''; 118 $n = 0; 119 foreach($this->columns as $col) { 120 $CN = 'C'.$n++; 121 122 if($col->isMulti()) { 123 $tn = 'M' . $col->getColref(); 124 $select .= "$tn.value AS $CN, "; 125 $from .= "\nLEFT OUTER JOIN multivals $tn"; 126 $from .= " ON DATA.pid = $tn.pid AND DATA.rev = $tn.rev"; 127 $from .= " AND $tn.tbl = '{$col->getTable()}' AND $tn.colref = {$col->getColref()}\n"; 128 } else { 129 $select .= 'data_'.$col->getTable().'.col' . $col->getColref() . " AS $CN, "; 130 } 131 } 132 $select = rtrim($select, ', '); 133 134 135 $sql = "SELECT $select\n FROM $from"; 136 return $sql; 137 } 138 139 /** 140 * Find a column to be used in the search 141 * 142 * @param string $colname may contain an alias 143 * @return bool|Column 144 */ 145 protected function findColumn($colname) { 146 if(!$this->schemas) throw new SearchException('noschemas'); 147 148 // resolve the alias or table name 149 list($table, $colname) = explode('.', $colname, 2); 150 if(!$colname) { 151 $colname = $table; 152 $table = ''; 153 } 154 if($table && isset($this->aliases[$table])) { 155 $table = $this->aliases[$table]; 156 } 157 158 if(!$colname) throw new SearchException('nocolname'); 159 160 // if table name given search only that, otherwiese try all for matching column name 161 if($table) { 162 $schemas = array($table => $this->schemas[$table]); 163 } else { 164 $schemas = $this->schemas; 165 } 166 167 // find it 168 $col = false; 169 foreach($schemas as $schema) { 170 $col = $schema->findColumn($colname); 171 if($col) break; 172 } 173 174 return $col; 175 } 176 177} 178 179/** 180 * Class SearchException 181 * 182 * A translatable exception 183 * 184 * @package plugin\struct\meta 185 */ 186class SearchException extends \RuntimeException { 187 public function __construct($message, $code = -1, Exception $previous = null) { 188 /** @var \action_plugin_struct_autoloader $plugin */ 189 $plugin = plugin_load('action', 'struct_autoloader'); 190 $trans = $plugin->getLang('searchex_' . $message); 191 if(!$trans) $trans = $message; 192 parent::__construct($trans, $code, $previous); 193 } 194} 195