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