xref: /plugin/struct/meta/Schema.php (revision d486d6d7d6c607c391a45aa431842e8fbc9d74c3)
1<?php
2
3namespace plugin\struct\meta;
4
5use plugin\struct\types\AbstractBaseType;
6
7/**
8 * Class Schema
9 *
10 * Represents the schema of a single data table and all its properties. It defines what can be stored in
11 * the represented data table and how those contents are formatted.
12 *
13 * It can be initialized with a timestamp to access the schema as it looked at that particular point in time.
14 *
15 * @package plugin\struct\meta
16 */
17class Schema {
18
19    /** @var \helper_plugin_sqlite|null */
20    protected $sqlite;
21
22    /** @var int The ID of this schema */
23    protected $id = 0;
24
25    /** @var string name of the associated table */
26    protected $table = '';
27
28    /**
29     * @var string the current checksum of this schema
30     */
31    protected $chksum = '';
32
33    /** @var Column[] all the colums */
34    protected $columns = array();
35
36    /** @var int */
37    protected $maxsort = 0;
38
39    /** @var int */
40    protected $ts = 0;
41
42    /** @var string struct version info */
43    protected $structversion = '?';
44
45    /**
46     * Schema constructor
47     *
48     * @param string $table The table this schema is for
49     * @param int $ts The timestamp for when this schema was valid, 0 for current
50     */
51    public function __construct($table, $ts = 0) {
52        /** @var \helper_plugin_struct_db $helper */
53        $helper = plugin_load('helper', 'struct_db');
54        $info = $helper->getInfo();
55        $this->structversion = $info['date'];
56        $this->sqlite = $helper->getDB();
57        if(!$this->sqlite) return;
58
59        $table = self::cleanTableName($table);
60        $this->table = $table;
61        $this->ts = $ts;
62
63        // load info about the schema itself
64        if($ts) {
65            $sql = "SELECT *
66                      FROM schemas
67                     WHERE tbl = ?
68                       AND ts <= ?
69                  ORDER BY ts DESC
70                     LIMIT 1";
71            $opt = array($table, $ts);
72        } else {
73            $sql = "SELECT *
74                      FROM schemas
75                     WHERE tbl = ?
76                  ORDER BY ts DESC
77                     LIMIT 1";
78            $opt = array($table);
79        }
80        $res = $this->sqlite->query($sql, $opt);
81        if($this->sqlite->res2count($res)) {
82            $schema = $this->sqlite->res2arr($res);
83            $result = array_shift($schema);
84            $this->id = $result['id'];
85            $this->chksum = $result['chksum'];
86        }
87        $this->sqlite->res_close($res);
88        if(!$this->id) return;
89
90        // load existing columns
91        $sql = "SELECT SC.*, T.*
92                  FROM schema_cols SC,
93                       types T
94                 WHERE SC.sid = ?
95                   AND SC.tid = T.id
96              ORDER BY SC.sort";
97        $res = $this->sqlite->query($sql, $this->id);
98        $rows = $this->sqlite->res2arr($res);
99        $this->sqlite->res_close($res);
100
101        foreach($rows as $row) {
102            $class = 'plugin\\struct\\types\\' . $row['class'];
103            if(!class_exists($class)) {
104                // This usually never happens, except during development
105                msg('Unknown type "' . hsc($row['class']) . '" falling back to Text', -1);
106                $class = 'plugin\\struct\\types\\Text';
107            }
108
109            $config = json_decode($row['config'], true);
110            /** @var AbstractBaseType $type */
111            $type = new $class($config, $row['label'], $row['ismulti'], $row['tid']);
112            $column = new Column(
113                $row['sort'],
114                $type,
115                $row['colref'],
116                $row['enabled'],
117                $table
118            );
119            $type->setContext($column);
120
121            $this->columns[$row['colref']] = $column;
122            if($row['sort'] > $this->maxsort) $this->maxsort = $row['sort'];
123        }
124    }
125
126    /**
127     * Cleans any unwanted stuff from table names
128     *
129     * @param string $table
130     * @return string
131     */
132    static public function cleanTableName($table) {
133        $table = strtolower($table);
134        $table = preg_replace('/[^a-z0-9_]+/', '', $table);
135        $table = preg_replace('/^[0-9_]+/', '', $table);
136        $table = trim($table);
137        return $table;
138    }
139
140    /**
141     * Gets a list of all available schemas
142     *
143     * @return string[]
144     */
145    static public function getAll() {
146        /** @var \helper_plugin_struct_db $helper */
147        $helper = plugin_load('helper', 'struct_db');
148        $db = $helper->getDB();
149        if(!$db) return array();
150
151        $res = $db->query("SELECT DISTINCT tbl FROM schemas ORDER BY tbl");
152        $tables = $db->res2arr($res);
153        $db->res_close($res);
154
155        $result = array();
156        foreach($tables as $row) {
157            $result[] = $row['tbl'];
158        }
159        return $result;
160    }
161
162    /**
163     * @return string
164     */
165    public function getChksum() {
166        return $this->chksum;
167    }
168
169    /**
170     * @return int
171     */
172    public function getId() {
173        return $this->id;
174    }
175
176    /**
177     * Returns a list of columns in this schema
178     *
179     * @param bool $withDisabled if false, disabled columns will not be returned
180     * @return Column[]
181     */
182    public function getColumns($withDisabled = true) {
183        if(!$withDisabled) {
184            return array_filter(
185                $this->columns,
186                function (Column $col) {
187                    return $col->isEnabled();
188                }
189            );
190        }
191
192        return $this->columns;
193    }
194
195    /**
196     * Find a column in the schema by its label
197     *
198     * Only enabled columns are returned!
199     *
200     * @param $name
201     * @return bool|Column
202     */
203    public function findColumn($name) {
204        foreach($this->columns as $col) {
205            if($col->isEnabled() && utf8_strtolower($col->getLabel()) == utf8_strtolower($name)) {
206                return $col;
207            }
208        }
209        return false;
210    }
211
212    /**
213     * @return string
214     */
215    public function getTable() {
216        return $this->table;
217    }
218
219    /**
220     * @return int the highest sort number used in this schema
221     */
222    public function getMaxsort() {
223        return $this->maxsort;
224    }
225
226    /**
227     * @return string the JSON representing this schema
228     */
229    public function toJSON() {
230        $data = array(
231            'structversion' => $this->structversion,
232            'schema' => $this->getTable(),
233            'id' => $this->getId(),
234            'columns' => array()
235        );
236
237        foreach($this->columns as $column) {
238            $data['columns'][] = array(
239                'colref' => $column->getColref(),
240                'ismulti' => $column->isMulti(),
241                'isenabled' => $column->isEnabled(),
242                'sort' => $column->getSort(),
243                'label' => $column->getLabel(),
244                'class' => $column->getType()->getClass(),
245                'config' => $column->getType()->getConfig(),
246            );
247        }
248
249        return json_encode($data, JSON_PRETTY_PRINT);
250    }
251}
252