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