xref: /plugin/struct/meta/AccessTableSerial.php (revision b9d35ff22b6a99d33aa2af304df64848b7983f3c)
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