xref: /plugin/struct/meta/Schema.php (revision 083afc55c907a52e46afc662d6f808a98112c779)
1<?php
2
3namespace plugin\struct\meta;
4
5use plugin\struct\types\AbstractBaseType;
6use plugin\struct\types\Text;
7
8class Schema {
9
10    /** @var \helper_plugin_sqlite|null */
11    protected $sqlite;
12
13    /** @var int The ID of this schema */
14    protected $id = 0;
15
16    /** @var string name of the associated table */
17    protected $table = '';
18
19    /**
20     * @var string the current checksum of this schema
21     */
22    protected $chksum = '';
23
24    /** @var AbstractBaseType[] all the colums */
25    protected $columns = array();
26
27    /** @var int */
28    protected $maxsort = 0;
29
30    /**
31     * Schema constructor
32     * @param string $table The table this schema is for
33     * @param int $ts The timestamp for when this schema was valid, 0 for current
34     */
35    public function __construct($table, $ts = 0) {
36        /** @var \helper_plugin_struct_db $helper */
37        $helper = plugin_load('helper', 'struct_db');
38        $this->sqlite = $helper->getDB();
39        if(!$this->sqlite) return;
40
41        $table = self::cleanTableName($table);
42        $this->table = $table;
43
44        // load info about the schema itself
45        if($ts) {
46            $sql = "SELECT *
47                      FROM schemas
48                     WHERE tbl = ?
49                       AND ts <= ?
50                  ORDER BY ts DESC
51                     LIMIT 1";
52            $opt = array($table, $ts);
53
54        } else {
55            $sql = "SELECT *
56                      FROM schemas
57                     WHERE tbl = ?
58                  ORDER BY ts DESC
59                     LIMIT 1";
60            $opt = array($table);
61        }
62        $res = $this->sqlite->query($sql, $opt);
63        if($this->sqlite->res2count($res)) {
64            $result = $this->sqlite->res2arr($res);
65            $this->id = $result['id'];
66            $this->chksum = $result['chksum'];
67
68        }
69        $this->sqlite->res_close($res);
70        if(!$this->id) return;
71
72        // load existing columns
73        $sql = "SELECT SC.*, T.*
74                  FROM schema_cols SC,
75                       types T
76                 WHERE SC.schema_id = ?
77                   AND SC.type_id = T.id
78              ORDER BY SC.sort";
79        $res = $this->sqlite->query($sql, $opt);
80        $rows = $this->sqlite->res2arr($res);
81        $this->sqlite->res_close($res);
82
83        foreach($rows as $row) {
84            $class = 'plugin\\struct\\type\\' . $row['class'];
85            $config = json_decode($row['config'], true);
86            $this->columns[$row['col']] = new $class($config, $row['label'], $row['ismulti']);
87            if($row['sort'] > $this->maxsort) $this->maxsort = $row['sort'];
88        }
89    }
90
91    /**
92     * Cleans any unwanted stuff from table names
93     *
94     * @param string $table
95     * @return string
96     */
97    static public function cleanTableName($table) {
98        $table = preg_replace('/[^a-z0-9_]+/', '', $table);
99        $table = preg_replace('/^[0-9_]+/', '', $table);
100        $table = trim($table);
101        return $table;
102    }
103
104    /**
105     * Returns a table to edit the schema
106     *
107     * @todo should this include the form?
108     *
109     * @return string
110     */
111    public function adminEditor() {
112        $html = '';
113
114        $html .= '<input type="hidden" name="table" value="' . hsc($this->table) . '">';
115
116        $html .= '<table class="inline">';
117        $html .= '<tr><th>Sort</th><th>Label</th><th>Multi-Input?</th><th>Configuration</th><th>Type</th></tr>'; // FIXME localize
118        foreach($this->columns as $key => $obj) {
119            $html .= $this->adminColumn($key, $obj);
120        }
121
122        // FIXME new one needs to be added dynamically, this is just for testing
123        $html .= $this->adminColumn('new1', new Text($this->maxsort+10));
124
125        $html .= '</table>';
126        return $html;
127    }
128
129    /**
130     * Returns the HTML to edit a single column definition of the schema
131     *
132     * @param string $column_id
133     * @param AbstractBaseType $type
134     * @return string
135     * @todo this should probably be reused for adding new columns via AJAX later?
136     */
137    protected function adminColumn($column_id, AbstractBaseType $type) {
138        $base = 'schema[' . $column_id . ']'; // base name for all fields
139
140        $html = '<tr>';
141
142        $html .= '<td>';
143        $html .= '<input type="text" name="' . $base . '[sort]" value="' . hsc($type->getSort()) . '" size="3">';
144        $html .= '</td>';
145
146        $html .= '<td>';
147        $html .= '<input type="text" name="' . $base . '[label]" value="' . hsc($type->getLabel()) . '">';
148        $html .= '</td>';
149
150        $html .= '<td>';
151        $checked = $type->isMulti() ? 'checked="checked"' : '';
152        $html .= '<input type="checkbox" name="' . $base . '[ismulti]" value="1" ' . $checked . '>';
153        $html .= '</td>';
154
155        $html .= '<td>';
156        $config = json_encode($type->getConfig(), JSON_PRETTY_PRINT);
157        $html .= '<textarea name="' . $base . '[config]" cols="45" rows="10">' . hsc($config) . '</textarea>';
158        $html .= '</td>';
159
160        $html .= '<td>';
161        $html .= substr(get_class($type), 20); //FIXME this needs to be a dropdown
162        $html .= '</td>';
163
164        $html .= '</tr>';
165
166        return $html;
167    }
168
169}
170