1f411d872SAndreas Gohr<?php 2f411d872SAndreas Gohr 3f411d872SAndreas Gohrnamespace dokuwiki\plugin\struct\meta; 4f411d872SAndreas Gohr 5c73fba38SAnna Dabrowska/** 6c73fba38SAnna Dabrowska * Class AccessTable 7c73fba38SAnna Dabrowska * 8c73fba38SAnna Dabrowska * Base class for data accessors 9c73fba38SAnna Dabrowska * 10c73fba38SAnna Dabrowska * @package dokuwiki\plugin\struct\meta 11c73fba38SAnna Dabrowska */ 12d6d97f60SAnna Dabrowskaabstract class AccessTable 13d6d97f60SAnna Dabrowska{ 14f411d872SAndreas Gohr 15efe74305SAnna Dabrowska const DEFAULT_REV = 0; 16efe74305SAnna Dabrowska const DEFAULT_LATEST = 1; 17efe74305SAnna Dabrowska 18f411d872SAndreas Gohr /** @var Schema */ 19f411d872SAndreas Gohr protected $schema; 20f411d872SAndreas Gohr protected $pid; 210ceefd5cSAnna Dabrowska protected $rid; 22aeb8444cSAnna Dabrowska protected $labels = []; 23f411d872SAndreas Gohr protected $ts = 0; 24f411d872SAndreas Gohr /** @var \helper_plugin_sqlite */ 25f411d872SAndreas Gohr protected $sqlite; 26f411d872SAndreas Gohr 2790421550SAndreas Gohr // options on how to retrieve data 28f411d872SAndreas Gohr protected $opt_skipempty = false; 29f411d872SAndreas Gohr 30f411d872SAndreas Gohr /** 31aeb8444cSAnna Dabrowska * @var string Name of single-value table 32aeb8444cSAnna Dabrowska */ 33aeb8444cSAnna Dabrowska protected $stable; 34aeb8444cSAnna Dabrowska 35aeb8444cSAnna Dabrowska /** 36aeb8444cSAnna Dabrowska * @var string Name of multi-value table 37aeb8444cSAnna Dabrowska */ 38aeb8444cSAnna Dabrowska protected $mtable; 39aeb8444cSAnna Dabrowska 40aeb8444cSAnna Dabrowska /** 41aeb8444cSAnna Dabrowska * @var array Column names for the single-value insert/update 42aeb8444cSAnna Dabrowska */ 43aeb8444cSAnna Dabrowska protected $singleCols; 44aeb8444cSAnna Dabrowska 45aeb8444cSAnna Dabrowska /** 46aeb8444cSAnna Dabrowska * @var array Input values for the single-value insert/update 47aeb8444cSAnna Dabrowska */ 48aeb8444cSAnna Dabrowska protected $singleValues; 49aeb8444cSAnna Dabrowska 50aeb8444cSAnna Dabrowska /** 51aeb8444cSAnna Dabrowska * @var array Input values for the multi-value inserts/updates 52aeb8444cSAnna Dabrowska */ 53aeb8444cSAnna Dabrowska protected $multiValues; 54aeb8444cSAnna Dabrowska 55*4cd5cc28SAnna Dabrowska public static function getPageAccess($tablename, $pid, $ts = 0) 56*4cd5cc28SAnna Dabrowska { 57*4cd5cc28SAnna Dabrowska $schema = new Schema($tablename, $ts); 58*4cd5cc28SAnna Dabrowska return new AccessTableData($schema, $pid, $ts, 0); 59*4cd5cc28SAnna Dabrowska } 60*4cd5cc28SAnna Dabrowska 61*4cd5cc28SAnna Dabrowska public static function getSerialAccess($tablename, $pid, $rid = 0) 62*4cd5cc28SAnna Dabrowska { 63*4cd5cc28SAnna Dabrowska $schema = new Schema($tablename, 0); 64*4cd5cc28SAnna Dabrowska return new AccessTableSerial($schema, $pid, 0, $rid); 65*4cd5cc28SAnna Dabrowska } 66*4cd5cc28SAnna Dabrowska 67*4cd5cc28SAnna Dabrowska public static function getLookupAccess($tablename, $rid = 0) 68*4cd5cc28SAnna Dabrowska { 69*4cd5cc28SAnna Dabrowska $schema = new Schema($tablename, 0); 70*4cd5cc28SAnna Dabrowska return new AccessTableLookup($schema, '', 0, $rid); 71*4cd5cc28SAnna Dabrowska } 72aeb8444cSAnna Dabrowska 73aeb8444cSAnna Dabrowska /** 74b9d35ff2SAnna Dabrowska * Factory method returning the appropriate data accessor (page, lookup or serial) 75f411d872SAndreas Gohr * 76*4cd5cc28SAnna Dabrowska * @deprecated 77f411d872SAndreas Gohr * @param Schema $schema schema to load 7886a40c1eSAnna Dabrowska * @param string $pid Page id to access 79a14cf85dSAnna Dabrowska * @param int $ts Time at which the data should be read or written 8086a40c1eSAnna Dabrowska * @param int $rid Row id, 0 for page type data, otherwise autoincrement 81aeb8444cSAnna Dabrowska * @return AccessTableData|AccessTableLookup 82f411d872SAndreas Gohr */ 83d6d97f60SAnna Dabrowska public static function bySchema(Schema $schema, $pid, $ts = 0, $rid = 0) 84d6d97f60SAnna Dabrowska { 85*4cd5cc28SAnna Dabrowska if (self::isTypePage($pid, $ts)) { 860ceefd5cSAnna Dabrowska return new AccessTableData($schema, $pid, $ts, $rid); 87f411d872SAndreas Gohr } 88aeb8444cSAnna Dabrowska return new AccessTableLookup($schema, $pid, $ts, $rid); 89aeb8444cSAnna Dabrowska } 90f411d872SAndreas Gohr 91f411d872SAndreas Gohr /** 92c73fba38SAnna Dabrowska * Factory Method to access data 93f411d872SAndreas Gohr * 94*4cd5cc28SAnna Dabrowska * @deprecated Use specific methods since we can no longer 95*4cd5cc28SAnna Dabrowska * guarantee instantiating the required descendant class 96f411d872SAndreas Gohr * @param string $tablename schema to load 9786a40c1eSAnna Dabrowska * @param string $pid Page id to access 98a14cf85dSAnna Dabrowska * @param int $ts Time at which the data should be read or written 9986a40c1eSAnna Dabrowska * @param int $rid Row id, 0 for page type data, otherwise autoincrement 100aeb8444cSAnna Dabrowska * @return AccessTableData|AccessTableLookup 101f411d872SAndreas Gohr */ 102d6d97f60SAnna Dabrowska public static function byTableName($tablename, $pid, $ts = 0, $rid = 0) 103d6d97f60SAnna Dabrowska { 10445698777SAnna Dabrowska // force loading the latest schema for anything other than page data, 10545698777SAnna Dabrowska // for which we might actually need the history 106*4cd5cc28SAnna Dabrowska if (!self::isTypePage($pid, $ts)) { 10745698777SAnna Dabrowska $schema = new Schema($tablename, time()); 10845698777SAnna Dabrowska } else { 109f411d872SAndreas Gohr $schema = new Schema($tablename, $ts); 11045698777SAnna Dabrowska } 1110ceefd5cSAnna Dabrowska return self::bySchema($schema, $pid, $ts, $rid); 112f411d872SAndreas Gohr } 113f411d872SAndreas Gohr 114f411d872SAndreas Gohr /** 115f411d872SAndreas Gohr * AccessTable constructor 116f411d872SAndreas Gohr * 117897aef42SAndreas Gohr * @param Schema $schema The schema valid at $ts 11886a40c1eSAnna Dabrowska * @param string $pid Page id 119897aef42SAndreas Gohr * @param int $ts Time at which the data should be read or written, 0 for now 1200ceefd5cSAnna Dabrowska * @param int $rid Row id: 0 for pages, autoincremented for other types 121f411d872SAndreas Gohr */ 122*4cd5cc28SAnna Dabrowska public function __construct($schema, $pid, $ts = 0, $rid = 0) 123d6d97f60SAnna Dabrowska { 124f411d872SAndreas Gohr /** @var \helper_plugin_struct_db $helper */ 125f411d872SAndreas Gohr $helper = plugin_load('helper', 'struct_db'); 126f411d872SAndreas Gohr $this->sqlite = $helper->getDB(); 127f411d872SAndreas Gohr 128f411d872SAndreas Gohr if (!$schema->getId()) { 129f411d872SAndreas Gohr throw new StructException('Schema does not exist. Only data of existing schemas can be accessed'); 130f411d872SAndreas Gohr } 131f411d872SAndreas Gohr 132f411d872SAndreas Gohr $this->schema = $schema; 133f411d872SAndreas Gohr $this->pid = $pid; 1340ceefd5cSAnna Dabrowska $this->rid = $rid; 135897aef42SAndreas Gohr $this->setTimestamp($ts); 136f411d872SAndreas Gohr foreach ($this->schema->getColumns() as $col) { 137f411d872SAndreas Gohr $this->labels[$col->getColref()] = $col->getType()->getLabel(); 138f411d872SAndreas Gohr } 139f411d872SAndreas Gohr } 140f411d872SAndreas Gohr 141f411d872SAndreas Gohr /** 142f411d872SAndreas Gohr * gives access to the schema 143f411d872SAndreas Gohr * 144f411d872SAndreas Gohr * @return Schema 145f411d872SAndreas Gohr */ 146d6d97f60SAnna Dabrowska public function getSchema() 147d6d97f60SAnna Dabrowska { 148f411d872SAndreas Gohr return $this->schema; 149f411d872SAndreas Gohr } 150f411d872SAndreas Gohr 151f411d872SAndreas Gohr /** 152f107f479SAndreas Gohr * The current pid 153f107f479SAndreas Gohr * 15486a40c1eSAnna Dabrowska * @return string 155f107f479SAndreas Gohr */ 156d6d97f60SAnna Dabrowska public function getPid() 157d6d97f60SAnna Dabrowska { 158f107f479SAndreas Gohr return $this->pid; 159f107f479SAndreas Gohr } 160f107f479SAndreas Gohr 161f107f479SAndreas Gohr /** 1620ceefd5cSAnna Dabrowska * The current rid 1630ceefd5cSAnna Dabrowska * 16486a40c1eSAnna Dabrowska * @return int 1650ceefd5cSAnna Dabrowska */ 166d6d97f60SAnna Dabrowska public function getRid() 167d6d97f60SAnna Dabrowska { 1680ceefd5cSAnna Dabrowska return $this->rid; 1690ceefd5cSAnna Dabrowska } 1700ceefd5cSAnna Dabrowska 1710ceefd5cSAnna Dabrowska /** 172f411d872SAndreas Gohr * Should remove the current data, by either deleting or ovewriting it 173f411d872SAndreas Gohr * 174f411d872SAndreas Gohr * @return bool if the delete succeeded 175f411d872SAndreas Gohr */ 176f411d872SAndreas Gohr abstract public function clearData(); 177f411d872SAndreas Gohr 178f411d872SAndreas Gohr /** 179f411d872SAndreas Gohr * Save the data to the database. 180f411d872SAndreas Gohr * 181f411d872SAndreas Gohr * We differentiate between single-value-column and multi-value-column by the value to the respective column-name, 182f411d872SAndreas Gohr * i.e. depending on if that is a string or an array, respectively. 183f411d872SAndreas Gohr * 1844e4edb41SAnna Dabrowska * @fixme we need a flag here to disable per-save db transactions if this is executed in bulk 185f411d872SAndreas Gohr * @param array $data typelabel => value for single fields or typelabel => array(value, value, ...) for multi fields 186f411d872SAndreas Gohr * @return bool success of saving the data to the database 187f411d872SAndreas Gohr */ 188aeb8444cSAnna Dabrowska public function saveData($data) 189aeb8444cSAnna Dabrowska { 190aeb8444cSAnna Dabrowska if (!$this->validateTypeData($data)) { 191aeb8444cSAnna Dabrowska return false; 192aeb8444cSAnna Dabrowska } 193aeb8444cSAnna Dabrowska 194aeb8444cSAnna Dabrowska $this->stable = 'data_' . $this->schema->getTable(); 195aeb8444cSAnna Dabrowska $this->mtable = 'multi_' . $this->schema->getTable(); 196aeb8444cSAnna Dabrowska 197aeb8444cSAnna Dabrowska $colrefs = array_flip($this->labels); 198aeb8444cSAnna Dabrowska 199aeb8444cSAnna Dabrowska foreach ($data as $colname => $value) { 200aeb8444cSAnna Dabrowska if (!isset($colrefs[$colname])) { 201aeb8444cSAnna Dabrowska throw new StructException("Unknown column %s in schema.", hsc($colname)); 202aeb8444cSAnna Dabrowska } 203aeb8444cSAnna Dabrowska 204aeb8444cSAnna Dabrowska $this->singleCols[] = 'col' . $colrefs[$colname]; 205aeb8444cSAnna Dabrowska if (is_array($value)) { 206aeb8444cSAnna Dabrowska foreach ($value as $index => $multivalue) { 207aeb8444cSAnna Dabrowska $this->multiValues[] = [$colrefs[$colname], $index + 1, $multivalue]; 208aeb8444cSAnna Dabrowska } 209aeb8444cSAnna Dabrowska // copy first value to the single column 210aeb8444cSAnna Dabrowska if (isset($value[0])) { 211aeb8444cSAnna Dabrowska $this->singleValues[] = $value[0]; 212aeb8444cSAnna Dabrowska } else { 213aeb8444cSAnna Dabrowska $this->singleValues[] = null; 214aeb8444cSAnna Dabrowska } 215aeb8444cSAnna Dabrowska } else { 216aeb8444cSAnna Dabrowska $this->singleValues[] = $value; 217aeb8444cSAnna Dabrowska } 218aeb8444cSAnna Dabrowska } 219aeb8444cSAnna Dabrowska 220aeb8444cSAnna Dabrowska $this->sqlite->query('BEGIN TRANSACTION'); 221aeb8444cSAnna Dabrowska 222aeb8444cSAnna Dabrowska $ok = $this->beforeSave(); 223aeb8444cSAnna Dabrowska 224aeb8444cSAnna Dabrowska // insert single values 225aeb8444cSAnna Dabrowska $ok = $ok && $this->sqlite->query( 226aeb8444cSAnna Dabrowska $this->getSingleSql(), 227aeb8444cSAnna Dabrowska array_merge($this->getSingleNoninputValues(), $this->singleValues) 228aeb8444cSAnna Dabrowska ); 229aeb8444cSAnna Dabrowska 230aeb8444cSAnna Dabrowska $ok = $ok && $this->afterSingleSave(); 231aeb8444cSAnna Dabrowska 232aeb8444cSAnna Dabrowska // insert multi values 233aeb8444cSAnna Dabrowska if ($ok && $this->multiValues) { 234aeb8444cSAnna Dabrowska $multisql = $this->getMultiSql(); 235aeb8444cSAnna Dabrowska $multiNoninputValues = $this->getMultiNoninputValues(); 236aeb8444cSAnna Dabrowska foreach ($this->multiValues as $value) { 237aeb8444cSAnna Dabrowska $ok = $ok && $this->sqlite->query( 238aeb8444cSAnna Dabrowska $multisql, 239aeb8444cSAnna Dabrowska array_merge($multiNoninputValues, $value) 240aeb8444cSAnna Dabrowska ); 241aeb8444cSAnna Dabrowska } 242aeb8444cSAnna Dabrowska } 243aeb8444cSAnna Dabrowska 244aeb8444cSAnna Dabrowska if (!$ok) { 245aeb8444cSAnna Dabrowska $this->sqlite->query('ROLLBACK TRANSACTION'); 246aeb8444cSAnna Dabrowska return false; 247aeb8444cSAnna Dabrowska } 248aeb8444cSAnna Dabrowska $this->sqlite->query('COMMIT TRANSACTION'); 249aeb8444cSAnna Dabrowska return true; 250aeb8444cSAnna Dabrowska } 251aeb8444cSAnna Dabrowska 252aeb8444cSAnna Dabrowska /** 253aeb8444cSAnna Dabrowska * Check whether all required data is present 254aeb8444cSAnna Dabrowska * 255aeb8444cSAnna Dabrowska * @param array $data 256aeb8444cSAnna Dabrowska * @return bool 257aeb8444cSAnna Dabrowska */ 258aeb8444cSAnna Dabrowska abstract protected function validateTypeData($data); 259aeb8444cSAnna Dabrowska 260aeb8444cSAnna Dabrowska /** 261aeb8444cSAnna Dabrowska * Names of non-input columns to be inserted into SQL query 262aeb8444cSAnna Dabrowska * 263aeb8444cSAnna Dabrowska * @return array 264aeb8444cSAnna Dabrowska */ 265aeb8444cSAnna Dabrowska abstract protected function getSingleNoninputCols(); 266aeb8444cSAnna Dabrowska 267aeb8444cSAnna Dabrowska /** 268aeb8444cSAnna Dabrowska * Values for non-input columns to be inserted into SQL query 269aeb8444cSAnna Dabrowska * for single-value tables 270aeb8444cSAnna Dabrowska * 271aeb8444cSAnna Dabrowska * @return array 272aeb8444cSAnna Dabrowska */ 273aeb8444cSAnna Dabrowska abstract protected function getSingleNoninputValues(); 274aeb8444cSAnna Dabrowska 275aeb8444cSAnna Dabrowska /** 276aeb8444cSAnna Dabrowska * String template for single-value table 277aeb8444cSAnna Dabrowska * 278aeb8444cSAnna Dabrowska * @return string 279aeb8444cSAnna Dabrowska */ 280aeb8444cSAnna Dabrowska protected function getSingleSql() 281aeb8444cSAnna Dabrowska { 282aeb8444cSAnna Dabrowska $cols = array_merge($this->getSingleNoninputCols(), $this->singleCols); 283aeb8444cSAnna Dabrowska $cols = join(',', $cols); 284aeb8444cSAnna Dabrowska $vals = array_merge($this->getSingleNoninputValues(), $this->singleValues); 285aeb8444cSAnna Dabrowska 286aeb8444cSAnna Dabrowska return "INSERT INTO $this->stable ($cols) VALUES (" . trim(str_repeat('?,', count($vals)), ',') . ');'; 287aeb8444cSAnna Dabrowska } 288aeb8444cSAnna Dabrowska 289aeb8444cSAnna Dabrowska /** 290aeb8444cSAnna Dabrowska * Optional operations to be executed before saving data 291aeb8444cSAnna Dabrowska * 292aeb8444cSAnna Dabrowska * @return bool False if any of the operations failed and transaction should be rolled back 293aeb8444cSAnna Dabrowska */ 294aeb8444cSAnna Dabrowska protected function beforeSave() 295aeb8444cSAnna Dabrowska { 296aeb8444cSAnna Dabrowska return true; 297aeb8444cSAnna Dabrowska } 298aeb8444cSAnna Dabrowska 299aeb8444cSAnna Dabrowska /** 300aeb8444cSAnna Dabrowska * Optional operations to be executed after saving data to single-value table, 301aeb8444cSAnna Dabrowska * before saving multivalues 302aeb8444cSAnna Dabrowska * 303aeb8444cSAnna Dabrowska * @return bool False if anything goes wrong and transaction should be rolled back 304aeb8444cSAnna Dabrowska */ 305aeb8444cSAnna Dabrowska protected function afterSingleSave() 306aeb8444cSAnna Dabrowska { 307aeb8444cSAnna Dabrowska return true; 308aeb8444cSAnna Dabrowska } 309aeb8444cSAnna Dabrowska 310aeb8444cSAnna Dabrowska /** 311aeb8444cSAnna Dabrowska * String template for multi-value table 312aeb8444cSAnna Dabrowska * 313aeb8444cSAnna Dabrowska * @return string 314aeb8444cSAnna Dabrowska */ 315aeb8444cSAnna Dabrowska abstract protected function getMultiSql(); 316aeb8444cSAnna Dabrowska 317aeb8444cSAnna Dabrowska /** 318aeb8444cSAnna Dabrowska * Values for non-input columns to be inserted into SQL query 319aeb8444cSAnna Dabrowska * for multi-value tables 320aeb8444cSAnna Dabrowska * @return array 321aeb8444cSAnna Dabrowska */ 322aeb8444cSAnna Dabrowska abstract protected function getMultiNoninputValues(); 323aeb8444cSAnna Dabrowska 324f411d872SAndreas Gohr 325f411d872SAndreas Gohr /** 326f411d872SAndreas Gohr * Should empty or invisible (inpage) fields be returned? 327f411d872SAndreas Gohr * 328f411d872SAndreas Gohr * Defaults to false 329f411d872SAndreas Gohr * 330f411d872SAndreas Gohr * @param null|bool $set new value, null to read only 331f411d872SAndreas Gohr * @return bool current value (after set) 332f411d872SAndreas Gohr */ 333d6d97f60SAnna Dabrowska public function optionSkipEmpty($set = null) 334d6d97f60SAnna Dabrowska { 335f411d872SAndreas Gohr if (!is_null($set)) { 336f411d872SAndreas Gohr $this->opt_skipempty = $set; 337f411d872SAndreas Gohr } 338f411d872SAndreas Gohr return $this->opt_skipempty; 339f411d872SAndreas Gohr } 340f411d872SAndreas Gohr 341f411d872SAndreas Gohr /** 342f411d872SAndreas Gohr * Get the value of a single column 343f411d872SAndreas Gohr * 344f411d872SAndreas Gohr * @param Column $column 345f411d872SAndreas Gohr * @return Value|null 346f411d872SAndreas Gohr */ 347d6d97f60SAnna Dabrowska public function getDataColumn($column) 348d6d97f60SAnna Dabrowska { 349f411d872SAndreas Gohr $data = $this->getData(); 350f411d872SAndreas Gohr foreach ($data as $value) { 351f411d872SAndreas Gohr if ($value->getColumn() == $column) { 352f411d872SAndreas Gohr return $value; 353f411d872SAndreas Gohr } 354f411d872SAndreas Gohr } 355f411d872SAndreas Gohr return null; 356f411d872SAndreas Gohr } 357f411d872SAndreas Gohr 358f411d872SAndreas Gohr /** 359f411d872SAndreas Gohr * returns the data saved for the page 360f411d872SAndreas Gohr * 361f411d872SAndreas Gohr * @return Value[] a list of values saved for the current page 362f411d872SAndreas Gohr */ 363d6d97f60SAnna Dabrowska public function getData() 364d6d97f60SAnna Dabrowska { 365f411d872SAndreas Gohr $data = $this->getDataFromDB(); 366f411d872SAndreas Gohr $data = $this->consolidateData($data, false); 367f411d872SAndreas Gohr return $data; 368f411d872SAndreas Gohr } 369f411d872SAndreas Gohr 370f411d872SAndreas Gohr /** 371f411d872SAndreas Gohr * returns the data saved for the page as associative array 372f411d872SAndreas Gohr * 373f411d872SAndreas Gohr * The array returned is in the same format as used in @see saveData() 374f411d872SAndreas Gohr * 37590421550SAndreas Gohr * It always returns raw Values! 37690421550SAndreas Gohr * 377f411d872SAndreas Gohr * @return array 378f411d872SAndreas Gohr */ 379d6d97f60SAnna Dabrowska public function getDataArray() 380d6d97f60SAnna Dabrowska { 381f411d872SAndreas Gohr $data = $this->getDataFromDB(); 382f411d872SAndreas Gohr $data = $this->consolidateData($data, true); 383f411d872SAndreas Gohr return $data; 384f411d872SAndreas Gohr } 385f411d872SAndreas Gohr 386f411d872SAndreas Gohr /** 387f411d872SAndreas Gohr * Return the data in pseudo syntax 388f411d872SAndreas Gohr */ 389d6d97f60SAnna Dabrowska public function getDataPseudoSyntax() 390d6d97f60SAnna Dabrowska { 391f411d872SAndreas Gohr $result = ''; 392a0a1d14eSAndreas Gohr $data = $this->getData(); 393a0a1d14eSAndreas Gohr 394a0a1d14eSAndreas Gohr foreach ($data as $value) { 395a0a1d14eSAndreas Gohr $key = $value->getColumn()->getFullQualifiedLabel(); 396a0a1d14eSAndreas Gohr $value = $value->getDisplayValue(); 397f411d872SAndreas Gohr if (is_array($value)) $value = join(', ', $value); 398f411d872SAndreas Gohr $result .= sprintf("% -20s : %s\n", $key, $value); 399f411d872SAndreas Gohr } 400f411d872SAndreas Gohr return $result; 401f411d872SAndreas Gohr } 402f411d872SAndreas Gohr 403f411d872SAndreas Gohr /** 404f411d872SAndreas Gohr * retrieve the data saved for the page from the database. Usually there is no need to call this function. 405f411d872SAndreas Gohr * Call @see SchemaData::getData instead. 406f411d872SAndreas Gohr */ 407d6d97f60SAnna Dabrowska protected function getDataFromDB() 408d6d97f60SAnna Dabrowska { 409*4cd5cc28SAnna Dabrowska $idColumn = self::isTypePage($this->pid, $this->ts) ? 'pid' : 'rid'; 410efe74305SAnna Dabrowska list($sql, $opt) = $this->buildGetDataSQL($idColumn); 411f411d872SAndreas Gohr 412f411d872SAndreas Gohr $res = $this->sqlite->query($sql, $opt); 413f411d872SAndreas Gohr $data = $this->sqlite->res2arr($res); 4149c00b26cSAndreas Gohr $this->sqlite->res_close($res); 415f411d872SAndreas Gohr return $data; 416f411d872SAndreas Gohr } 417f411d872SAndreas Gohr 418f411d872SAndreas Gohr /** 419f411d872SAndreas Gohr * Creates a proper result array from the database data 420f411d872SAndreas Gohr * 421f411d872SAndreas Gohr * @param array $DBdata the data as it is retrieved from the database, i.e. by SchemaData::getDataFromDB 422f411d872SAndreas Gohr * @param bool $asarray return data as associative array (true) or as array of Values (false) 423f411d872SAndreas Gohr * @return array|Value[] 424f411d872SAndreas Gohr */ 425d6d97f60SAnna Dabrowska protected function consolidateData($DBdata, $asarray = false) 426d6d97f60SAnna Dabrowska { 427f411d872SAndreas Gohr $data = array(); 428f411d872SAndreas Gohr 429f411d872SAndreas Gohr $sep = Search::CONCAT_SEPARATOR; 430f411d872SAndreas Gohr 431f411d872SAndreas Gohr foreach ($this->schema->getColumns(false) as $col) { 43290421550SAndreas Gohr // if no data saved yet, return empty strings 433f411d872SAndreas Gohr if ($DBdata) { 434bab52340SAndreas Gohr $val = $DBdata[0]['out' . $col->getColref()]; 435f411d872SAndreas Gohr } else { 436f411d872SAndreas Gohr $val = ''; 437f411d872SAndreas Gohr } 438f411d872SAndreas Gohr 439f411d872SAndreas Gohr // multi val data is concatenated 440f411d872SAndreas Gohr if ($col->isMulti()) { 441f411d872SAndreas Gohr $val = explode($sep, $val); 442f411d872SAndreas Gohr $val = array_filter($val); 443f411d872SAndreas Gohr } 444f411d872SAndreas Gohr 44590421550SAndreas Gohr $value = new Value($col, $val); 446f411d872SAndreas Gohr 44790421550SAndreas Gohr if ($this->opt_skipempty && $value->isEmpty()) continue; 44890421550SAndreas Gohr if ($this->opt_skipempty && !$col->isVisibleInPage()) continue; //FIXME is this a correct assumption? 44990421550SAndreas Gohr 45090421550SAndreas Gohr // for arrays, we return the raw value only 451f411d872SAndreas Gohr if ($asarray) { 45290421550SAndreas Gohr $data[$col->getLabel()] = $value->getRawValue(); 453f411d872SAndreas Gohr } else { 4546e54daafSMichael Große $data[$col->getLabel()] = $value; 455f411d872SAndreas Gohr } 456f411d872SAndreas Gohr } 457f411d872SAndreas Gohr 458f411d872SAndreas Gohr return $data; 459f411d872SAndreas Gohr } 460f411d872SAndreas Gohr 461f411d872SAndreas Gohr /** 462f411d872SAndreas Gohr * Builds the SQL statement to select the data for this page and schema 463f411d872SAndreas Gohr * 464f411d872SAndreas Gohr * @return array Two fields: the SQL string and the parameters array 465f411d872SAndreas Gohr */ 466d6d97f60SAnna Dabrowska protected function buildGetDataSQL($idColumn = 'pid') 467d6d97f60SAnna Dabrowska { 468f411d872SAndreas Gohr $sep = Search::CONCAT_SEPARATOR; 469f411d872SAndreas Gohr $stable = 'data_' . $this->schema->getTable(); 470f411d872SAndreas Gohr $mtable = 'multi_' . $this->schema->getTable(); 471f411d872SAndreas Gohr 472f411d872SAndreas Gohr $QB = new QueryBuilder(); 473f411d872SAndreas Gohr $QB->addTable($stable, 'DATA'); 4746fd73b4bSAnna Dabrowska $QB->addSelectColumn('DATA', $idColumn, strtoupper($idColumn)); 4756fd73b4bSAnna Dabrowska $QB->addGroupByStatement("DATA.$idColumn"); 476f411d872SAndreas Gohr 477f411d872SAndreas Gohr foreach ($this->schema->getColumns(false) as $col) { 478f411d872SAndreas Gohr $colref = $col->getColref(); 479f411d872SAndreas Gohr $colname = 'col' . $colref; 480bab52340SAndreas Gohr $outname = 'out' . $colref; 481f411d872SAndreas Gohr 482f411d872SAndreas Gohr if ($col->getType()->isMulti()) { 483f411d872SAndreas Gohr $tn = 'M' . $colref; 484f411d872SAndreas Gohr $QB->addLeftJoin( 485f411d872SAndreas Gohr 'DATA', 486f411d872SAndreas Gohr $mtable, 487f411d872SAndreas Gohr $tn, 4886fd73b4bSAnna Dabrowska "DATA.$idColumn = $tn.$idColumn AND DATA.rev = $tn.rev AND $tn.colref = $colref" 489f411d872SAndreas Gohr ); 490bab52340SAndreas Gohr $col->getType()->select($QB, $tn, 'value', $outname); 491bab52340SAndreas Gohr $sel = $QB->getSelectStatement($outname); 492bab52340SAndreas Gohr $QB->addSelectStatement("GROUP_CONCAT($sel, '$sep')", $outname); 493f411d872SAndreas Gohr } else { 494bab52340SAndreas Gohr $col->getType()->select($QB, 'DATA', $colname, $outname); 495bab52340SAndreas Gohr $QB->addGroupByStatement($outname); 496f411d872SAndreas Gohr } 497f411d872SAndreas Gohr } 498f411d872SAndreas Gohr 4996fd73b4bSAnna Dabrowska $pl = $QB->addValue($this->{$idColumn}); 5006fd73b4bSAnna Dabrowska $QB->filters()->whereAnd("DATA.$idColumn = $pl"); 501897aef42SAndreas Gohr $pl = $QB->addValue($this->getLastRevisionTimestamp()); 502f411d872SAndreas Gohr $QB->filters()->whereAnd("DATA.rev = $pl"); 503f411d872SAndreas Gohr 504f411d872SAndreas Gohr return $QB->getSQL(); 505f411d872SAndreas Gohr } 506f411d872SAndreas Gohr 507f411d872SAndreas Gohr /** 50813eddb0fSAndreas Gohr * @param int $ts 50913eddb0fSAndreas Gohr */ 510d6d97f60SAnna Dabrowska public function setTimestamp($ts) 511d6d97f60SAnna Dabrowska { 512897aef42SAndreas Gohr if ($ts && $ts < $this->schema->getTimeStamp()) { 513897aef42SAndreas Gohr throw new StructException('Given timestamp is not valid for current Schema'); 514897aef42SAndreas Gohr } 515897aef42SAndreas Gohr 51613eddb0fSAndreas Gohr $this->ts = $ts; 51713eddb0fSAndreas Gohr } 51813eddb0fSAndreas Gohr 51913eddb0fSAndreas Gohr /** 52069f7ec8fSAnna Dabrowska * Returns the timestamp from the current data 52169f7ec8fSAnna Dabrowska * @return int 52269f7ec8fSAnna Dabrowska */ 52369f7ec8fSAnna Dabrowska public function getTimestamp() 52469f7ec8fSAnna Dabrowska { 52569f7ec8fSAnna Dabrowska return $this->ts; 52669f7ec8fSAnna Dabrowska } 52769f7ec8fSAnna Dabrowska 52869f7ec8fSAnna Dabrowska /** 529897aef42SAndreas Gohr * Return the last time an edit happened for this table for the currently set 530a14cf85dSAnna Dabrowska * time and pid. Used in @see buildGetDataSQL() 531f411d872SAndreas Gohr * 532897aef42SAndreas Gohr * @return int 533f411d872SAndreas Gohr */ 534897aef42SAndreas Gohr abstract protected function getLastRevisionTimestamp(); 53587dc1344SAndreas Gohr 53687dc1344SAndreas Gohr /** 53787dc1344SAndreas Gohr * Check if the given data validates against the current types. 53887dc1344SAndreas Gohr * 53987dc1344SAndreas Gohr * @param array $data 54093ca6f4fSAndreas Gohr * @return AccessDataValidator 54187dc1344SAndreas Gohr */ 542d6d97f60SAnna Dabrowska public function getValidator($data) 543d6d97f60SAnna Dabrowska { 54493ca6f4fSAndreas Gohr return new AccessDataValidator($this, $data); 54587dc1344SAndreas Gohr } 546c73fba38SAnna Dabrowska 547c73fba38SAnna Dabrowska /** 548c73fba38SAnna Dabrowska * Returns true if data is of type "page" 549c73fba38SAnna Dabrowska * 550c73fba38SAnna Dabrowska * @param string $pid 551c73fba38SAnna Dabrowska * @param int $rev 552c73fba38SAnna Dabrowska * @param int $rid 553c73fba38SAnna Dabrowska * @return bool 554c73fba38SAnna Dabrowska */ 555*4cd5cc28SAnna Dabrowska public static function isTypePage($pid, $rev) 556c73fba38SAnna Dabrowska { 557c73fba38SAnna Dabrowska return $rev > 0; 558c73fba38SAnna Dabrowska } 559c73fba38SAnna Dabrowska 560c73fba38SAnna Dabrowska /** 561c73fba38SAnna Dabrowska * Returns true if data is of type "lookup" 562c73fba38SAnna Dabrowska * 563c73fba38SAnna Dabrowska * @param string $pid 564c73fba38SAnna Dabrowska * @param int $rev 565c73fba38SAnna Dabrowska * @param int $rid 566c73fba38SAnna Dabrowska * @return bool 567c73fba38SAnna Dabrowska */ 568*4cd5cc28SAnna Dabrowska public static function isTypeLookup($pid, $rev) 569c73fba38SAnna Dabrowska { 570c73fba38SAnna Dabrowska return $pid === ''; 571c73fba38SAnna Dabrowska } 572c73fba38SAnna Dabrowska 573c73fba38SAnna Dabrowska /** 574c73fba38SAnna Dabrowska * Returns true if data is of type "serial" 575c73fba38SAnna Dabrowska * 576c73fba38SAnna Dabrowska * @param string $pid 577c73fba38SAnna Dabrowska * @param int $rev 578c73fba38SAnna Dabrowska * @param int $rid 579c73fba38SAnna Dabrowska * @return bool 580c73fba38SAnna Dabrowska */ 581*4cd5cc28SAnna Dabrowska public static function isTypeSerial($pid, $rev) 582c73fba38SAnna Dabrowska { 583c73fba38SAnna Dabrowska return $pid !== '' && $rev === 0; 584c73fba38SAnna Dabrowska } 585f411d872SAndreas Gohr} 586