1<?php 2 3namespace dokuwiki\plugin\struct\meta; 4 5/** 6 * Class AccessTableSerial 7 * 8 * Load and (more importantly) save serial data 9 * 10 * @package dokuwiki\plugin\struct\meta 11 */ 12class AccessTableSerial extends AccessTable { 13 14 /** 15 * AccessTableSerial constructor. 16 * 17 * @param Schema $schema Which schema to access 18 * @param int $pid the row identifier (0 for new row) 19 * @param int $ts Time at which the data should be read or written, 0 for now 20 * @param int $rid 21 */ 22 public function __construct(Schema $schema, $pid = 0, $ts = 0, $rid = 0) { 23 parent::__construct($schema, $pid, $ts, $rid); 24 } 25 26 /** 27 * Remove the current data 28 */ 29 public function clearData() { 30 if(!$this->rid || !$this->pid) return; // no data 31 32 /** @noinspection SqlResolve */ 33 $sql = 'DELETE FROM ? WHERE pid = ? AND rid = ?'; 34 $this->sqlite->query($sql, 'data_'.$this->schema->getTable(), $this->pid, $this->rid); 35 $this->sqlite->query($sql, 'multi_'.$this->schema->getTable(), $this->pid, $this->rid); 36 } 37 38 /** 39 * Save the data to the database. 40 * 41 * We differentiate between single-value-column and multi-value-column by the value to the respective column-name, 42 * i.e. depending on if that is a string or an array, respectively. 43 * 44 * @param array $data typelabel => value for single fields or typelabel => array(value, value, ...) for multi fields 45 * @return bool success of saving the data to the database 46 * @todo this duplicates quite a bit code from AccessTableData - could we avoid that? 47 */ 48 public function saveData($data) { 49 $stable = 'data_' . $this->schema->getTable(); 50 $mtable = 'multi_' . $this->schema->getTable(); 51 52 // we do not store completely empty rows 53 $isempty = array_reduce($data, function ($isempty, $cell) { 54 return $isempty && ($cell === '' || $cell === array() || $cell === null); 55 }, true); 56 if($isempty) return false; 57 58 $singlecols = ['pid', 'rev', 'latest']; 59 $opt = [$this->pid, 0, 1]; 60 61 $colrefs = array_flip($this->labels); 62 $multiopts = array(); 63 foreach($data as $colname => $value) { 64 if(!isset($colrefs[$colname])) { 65 throw new StructException("Unknown column %s in schema.", hsc($colname)); 66 } 67 68 $singlecols[] = 'col' . $colrefs[$colname]; 69 if(is_array($value)) { 70 foreach($value as $index => $multivalue) { 71 $multiopts[] = array($colrefs[$colname], $index + 1, $multivalue,); 72 } 73 // copy first value to the single column 74 if(isset($value[0])) { 75 $opt[] = $value[0]; 76 } else { 77 $opt[] = null; 78 } 79 } else { 80 $opt[] = $value; 81 } 82 } 83 84 // FIXME select pid too 85 $ridSingle = "(SELECT (COALESCE(MAX(rid), 0 ) + 1) FROM $stable)"; 86 $ridMulti = "(SELECT (COALESCE(MAX(rid), 0 ) + 1) FROM $mtable)"; 87 88 $singlesql = "REPLACE INTO $stable (rid, " . join(',', $singlecols) . ") VALUES ($ridSingle, " . trim(str_repeat('?,', count($opt)), ',') . ")"; 89 /** @noinspection SqlResolve */ 90 $multisql = "REPLACE INTO $mtable (rid, colref, row, value) VALUES ($ridMulti,?,?,?,?)"; 91 92 $this->sqlite->query('BEGIN TRANSACTION'); 93 $ok = true; 94 95 // insert single values 96 $ok = $ok && $this->sqlite->query($singlesql, $opt); 97 98 // get new rid if this is a new insert 99 if($ok && !$this->rid) { 100 $res = $this->sqlite->query('SELECT last_insert_rowid()'); 101 $this->rid = $this->sqlite->res2single($res); 102 $this->sqlite->res_close($res); 103 if(!$this->rid) $ok = false; 104 } 105 106 // insert multi values 107 if($ok) foreach($multiopts as $multiopt) { 108 $multiopt = array_merge(array($this->rid,$this->pid), $multiopt); 109 $ok = $ok && $this->sqlite->query($multisql, $multiopt); 110 } 111 112 if(!$ok) { 113 $this->sqlite->query('ROLLBACK TRANSACTION'); 114 return false; 115 } 116 $this->sqlite->query('COMMIT TRANSACTION'); 117 return true; 118 } 119 120 protected function getLastRevisionTimestamp() { 121 return 0; 122 } 123 124} 125