1<?php 2 3namespace plugin\struct\meta; 4use plugin\struct\types\AbstractBaseType; 5 6/** 7 * Class Schema 8 * 9 * Represents the schema of a single data table and all its properties. It defines what can be stored in 10 * the represented data table and how those contents are formatted. 11 * 12 * It can be initialized with a timestamp to access the schema as it looked at that particular point in time. 13 * 14 * @package plugin\struct\meta 15 */ 16class Schema { 17 18 /** @var \helper_plugin_sqlite|null */ 19 protected $sqlite; 20 21 /** @var int The ID of this schema */ 22 protected $id = 0; 23 24 /** @var string name of the associated table */ 25 protected $table = ''; 26 27 /** 28 * @var string the current checksum of this schema 29 */ 30 protected $chksum = ''; 31 32 /** @var Column[] all the colums */ 33 protected $columns = array(); 34 35 /** @var int */ 36 protected $maxsort = 0; 37 38 /** @var int */ 39 protected $ts = 0; 40 41 /** 42 * Schema constructor 43 * 44 * @param string $table The table this schema is for 45 * @param int $ts The timestamp for when this schema was valid, 0 for current 46 */ 47 public function __construct($table, $ts = 0) { 48 /** @var \helper_plugin_struct_db $helper */ 49 $helper = plugin_load('helper', 'struct_db'); 50 $this->sqlite = $helper->getDB(); 51 if(!$this->sqlite) return; 52 53 $table = self::cleanTableName($table); 54 $this->table = $table; 55 $this->ts = $ts; 56 57 // load info about the schema itself 58 if($ts) { 59 $sql = "SELECT * 60 FROM schemas 61 WHERE tbl = ? 62 AND ts <= ? 63 ORDER BY ts DESC 64 LIMIT 1"; 65 $opt = array($table, $ts); 66 } else { 67 $sql = "SELECT * 68 FROM schemas 69 WHERE tbl = ? 70 ORDER BY ts DESC 71 LIMIT 1"; 72 $opt = array($table); 73 } 74 $res = $this->sqlite->query($sql, $opt); 75 if($this->sqlite->res2count($res)) { 76 $schema = $this->sqlite->res2arr($res); 77 $result = array_shift($schema); 78 $this->id = $result['id']; 79 $this->chksum = $result['chksum']; 80 81 } 82 $this->sqlite->res_close($res); 83 if(!$this->id) return; 84 85 // load existing columns 86 $sql = "SELECT SC.*, T.* 87 FROM schema_cols SC, 88 types T 89 WHERE SC.sid = ? 90 AND SC.tid = T.id 91 ORDER BY SC.sort"; 92 $res = $this->sqlite->query($sql, $this->id); 93 $rows = $this->sqlite->res2arr($res); 94 $this->sqlite->res_close($res); 95 96 foreach($rows as $row) { 97 $class = 'plugin\\struct\\types\\' . $row['class']; 98 if(!class_exists($class)) { 99 // This usually never happens, except during development 100 msg('Unknown type "'.hsc($row['class']).'" falling back to Text', -1); 101 $class = 'plugin\\struct\\types\\Text'; 102 } 103 104 $config = json_decode($row['config'], true); 105 /** @var AbstractBaseType $type */ 106 $type = new $class($config, $row['label'], $row['ismulti'], $row['tid']); 107 $column = new Column( 108 $row['sort'], 109 $type, 110 $row['colref'], 111 $row['enabled'], 112 $table 113 ); 114 $type->setContext($column); 115 116 $this->columns[$row['colref']] = $column; 117 if($row['sort'] > $this->maxsort) $this->maxsort = $row['sort']; 118 } 119 } 120 121 /** 122 * Cleans any unwanted stuff from table names 123 * 124 * @param string $table 125 * @return string 126 */ 127 static public function cleanTableName($table) { 128 $table = strtolower($table); 129 $table = preg_replace('/[^a-z0-9_]+/', '', $table); 130 $table = preg_replace('/^[0-9_]+/', '', $table); 131 $table = trim($table); 132 return $table; 133 } 134 135 /** 136 * Gets a list of all available schemas 137 * 138 * @return string[] 139 */ 140 static public function getAll() { 141 /** @var \helper_plugin_struct_db $helper */ 142 $helper = plugin_load('helper', 'struct_db'); 143 $db = $helper->getDB(); 144 if(!$db) return array(); 145 146 $res = $db->query("SELECT DISTINCT tbl FROM schemas ORDER BY tbl"); 147 $tables = $db->res2arr($res); 148 $db->res_close($res); 149 150 $result = array(); 151 foreach($tables as $row) { 152 $result[] = $row['tbl']; 153 } 154 return $result; 155 } 156 157 /** 158 * @return string 159 */ 160 public function getChksum() { 161 return $this->chksum; 162 } 163 164 /** 165 * @return int 166 */ 167 public function getId() { 168 return $this->id; 169 } 170 171 /** 172 * Returns a list of columns in this schema 173 * 174 * @param bool $withDisabled if false, disabled columns will not be returned 175 * @return Column[] 176 */ 177 public function getColumns($withDisabled = true) { 178 if(!$withDisabled) { 179 return array_filter( 180 $this->columns, 181 function (Column $col) { 182 return $col->isEnabled(); 183 } 184 ); 185 } 186 187 return $this->columns; 188 } 189 190 /** 191 * Find a column in the schema by its label 192 * 193 * Only enabled columns are returned! 194 * 195 * @param $name 196 * @return bool|Column 197 */ 198 public function findColumn($name) { 199 foreach($this->columns as $col) { 200 if($col->isEnabled() && utf8_strtolower($col->getLabel()) == utf8_strtolower($name)) { 201 return $col; 202 } 203 } 204 return false; 205 } 206 207 /** 208 * @return string 209 */ 210 public function getTable() { 211 return $this->table; 212 } 213 214 /** 215 * @return int the highest sort number used in this schema 216 */ 217 public function getMaxsort() { 218 return $this->maxsort; 219 } 220 221} 222