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