24 if(!$this->databaseStructureFilename)
throw new Exception(
'No database structure filename');
25 $table_names = $structure = $collations = $warnings = [];
26 $all_caps = !$table_trans && is_file($this->databaseStructureFilename);
27 if($all_caps)
foreach(array_keys($this->def) as $table_name)
28 if(strtoupper($table_names[strtolower($table_name)] = $table_name) != $table_name) $all_caps =
false;
29 if($all_caps) $table_trans = \Rsi\Str::TRANSFORM_UPPER;
31 foreach($db->tables() as $table_name){
33 foreach($db->columns($table_name) as $column_name => $column){
34 switch($default = $column[
'default']){
35 case 'NULL': $default = null;
break;
37 if(preg_match(
'/^\'(.*)\'$/',$default,$match)) $default = $match[1];
40 Widget::REQUIRED => (bool)$column[
'required'],
41 Widget::DEFAULT_VALUE => $default,
42 'primary' => (
bool)$column[
'primary'],
43 'ref' => $column[
'ref']
45 $type = $column[
'type'];
46 if(substr($type,-3) ==
'int'){
47 $def[
'type'] =
'number';
48 $def[Widget::MAX] = $max = pow(10,$column[
'precision']) - 1;
49 $def[Widget::MIN] = $column[
'unsigned'] ? 0 : -$max;
51 elseif(substr($type,-4) ==
'text'){
52 $def[
'type'] =
'memo';
53 $def[Widget::MAX] = (int)$column[
'length'];
57 $def[
'type'] = $column[
'length'] > $this->charLimit ?
'memo' :
'char';
58 $def[Widget::MAX] = (int)$column[
'length'];
63 $def[
'type'] =
'number';
64 if(($scale = $column[
'scale']) !== null){
65 $def[
'decimals'] = $scale;
66 $def[Widget::MIN] = -($def[Widget::MAX] = pow(10,$column[
'precision'] - $scale) - pow(10,-$scale));
72 $table[$column_name] = array_filter($def) + \Rsi\Record::select($def,[Widget::MIN,Widget::MAX]);
73 if($collation = $column[
'collation']){
74 if(!array_key_exists($collation,$collations)) $collations[$collation] = [];
75 $collations[$collation][] = $table_name .
'.' . $column_name;
78 $structure[$table_names[strtolower($table_name)] ?? \Rsi\Str::transform($table_name,$table_trans)] = $table;
81 $this->databaseStructureFilename,
83 "//this database-structure file was auto-generated on " . date(
'Y-m-d H:i:s') .
"\n" .
84 "//it may be overwritten in the future -- changes will NOT be saved\n" .
85 "//use '{$this->filename}' instead for adjustments\n\n" .
86 preg_replace(
'/\\s+array \\(/',
' array(',
'return ' . var_export($structure,
true) .
';'),
89 if(count($collations) > 1){
90 uasort($collations,
function($a,$b){
91 if(($ac = count($a)) == ($bc = count($b)))
return strcmp(array_shift($a),array_shift($b));
92 return $ac < $bc ? 1 : -1;
94 $default = \Rsi\Record::key($collations);
95 array_shift($collations);
96 $this->
component(
'log')->warning($warning =
"Columns with collation other than '$default'",[
'collations' => $collations]);
97 foreach($collations as $collation => $columns) $warning .=
"\n- $collation: " . implode(
', ',$columns);
98 $warnings[] = $warning;
108 return \Rsi\Record::iget($this->def,$table);
115 public function key($table){
117 foreach($this->
table($table) as $column => $def)
if($def[
'primary'] ??
false) $key[] = $column;
127 public function column($table,$column,$extra = null){
128 $def = array_merge(\
Rsi\Record::iget($this->
table($table),$column,[]),$extra ?: []);
129 if(array_key_exists(null,$def)){
130 $ref = \Rsi\Record::explode($def[null]);
131 $column = array_pop($ref);
132 $def += $this->
column($ref ? array_pop($ref) : $table,$column);
135 foreach($def as $key => $value)
if(substr($key,0,1) ==
'@'){
137 $def[substr($key,1)] = call_user_func($value);
148 public function value($table,$column,$key,$default = null){
149 return \Rsi\Record::get($this->
column($table,$column),$key,$default);
157 public function ref($table,$column){
158 if($ref = \
Rsi\Record::iget($this->
table($table),[$column,null])){
159 $column = array_pop($ref);
160 return $this->
ref($ref ? array_pop($ref) : $table,$column) ?: $column;
171 if($value !== null)
switch($type){
173 case 'number':
return $value + 0;
185 foreach($tables as $table)
if($def = $this->
column($table,$column))
186 return $this->
convert($def[
'type'] ?? null,$value);
196 if($record)
foreach($record as $column => &$value) $value = $this->
convertColumn($column,$value,$tables);
207 if($value !== null)
switch($type){
209 case 'date':
return is_numeric($value) ? $this->
component(
'db')->date($value) : $value;
210 case 'datetime':
return is_numeric($value) ? $this->
component(
'db')->dateTime($value) : $value;
222 foreach($tables as $table)
if($def = $this->
column($table,$column))
223 return $this->
format($def[
'type'] ?? null,$value);
233 if($record)
foreach($record as $column => &$value)
foreach($tables as $table)
if($column = $this->
column($table,$column)){
234 $value = $this->
format($column[
'type'] ?? null,$value);
242 if($this->_def === null){
244 if($this->databaseStructureFilename) $this->_def = array_replace_recursive($this->_def,require($this->databaseStructureFilename));
245 if($this->filename) $this->_def = array_replace_recursive($this->_def,require($this->filename));
250 public function __call($func_name,$params){
251 return $this->
column($func_name,array_shift($params),array_shift($params));
formatRecord($record, $tables)
Format a value from standard, internal format to database format.
generateDatabaseStructure($table_trans=null)
Generate a definition file from the database structure.
value($table, $column, $key, $default=null)
Definition value.
__call($func_name, $params)
ref($table, $column)
Get the base (referenced) column.
convertColumn($column, $value, $tables)
Convert a single column value from database format to standard, internal format.
key($table)
Primary key columns.
formatColumn($column, $value, $tables)
Convert a single column value from standard, internal format to database format.
$databaseStructureFilename
column($table, $column, $extra=null)
Column definition.
convertRecord($record, $tables)
Convert a record from database format to standard, internal format.
format($type, $value)
Format a value from standard, internal format to database format.
convert($type, $value)
Convert a single value from database format to standard, internal format.
table($table)
Table definition (all columns).
component($name)
Get a component (local or default).