FRED™  3.0
FRED™: Framework for Rapid and Easy Development
Def.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Rsi\Fred;
4 
6 
7 class Def extends Component{
8 
10  public $filename = null;
11 
12  public $boolTrue = 1;
13  public $boolFalse = 0;
14  public $charLimit = 128;
15 
16  protected $_def = null;
17 
18  /**
19  * Generate a definition file from the database structure.
20  * @param string $table_trans Transformation method to use for the table name.
21  * @see \\Rsi\\Str::transform()
22  */
23  public function generateDatabaseStructure($table_trans = null){
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;
30  $db = $this->component('db');
31  foreach($db->tables() as $table_name){
32  $table = [];
33  foreach($db->columns($table_name) as $column_name => $column){
34  switch($default = $column['default']){
35  case 'NULL': $default = null; break;
36  default:
37  if(preg_match('/^\'(.*)\'$/',$default,$match)) $default = $match[1];
38  }
39  $def = [
40  Widget::REQUIRED => (bool)$column['required'],
41  Widget::DEFAULT_VALUE => $default,
42  'primary' => (bool)$column['primary'],
43  'ref' => $column['ref']
44  ];
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;
50  }
51  elseif(substr($type,-4) == 'text'){
52  $def['type'] = 'memo';
53  $def[Widget::MAX] = (int)$column['length'];
54  }
55  else switch($type){
56  case 'varchar':
57  $def['type'] = $column['length'] > $this->charLimit ? 'memo' : 'char';
58  $def[Widget::MAX] = (int)$column['length'];
59  break;
60  case 'decimal':
61  case 'float':
62  case 'double':
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));
67  }
68  break;
69  default:
70  $def['type'] = $type;
71  }
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;
76  }
77  }
78  $structure[$table_names[strtolower($table_name)] ?? \Rsi\Str::transform($table_name,$table_trans)] = $table;
79  }
80  \Rsi\File::write(
81  $this->databaseStructureFilename,
82  "<?php\n\n" .
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) . ';'),
87  0666
88  );
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;
93  });
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;
99  }
100  return $warnings;
101  }
102  /**
103  * Table definition (all columns).
104  * @param string $table
105  * @return array Key = column, value = definition.
106  */
107  public function table($table){
108  return \Rsi\Record::iget($this->def,$table);
109  }
110  /**
111  * Primary key columns.
112  * @param string $table
113  * @return array
114  */
115  public function key($table){
116  $key = [];
117  foreach($this->table($table) as $column => $def) if($def['primary'] ?? false) $key[] = $column;
118  return $key;
119  }
120  /**
121  * Column definition.
122  * @param string $table Table name.
123  * @param string $column Column name.
124  * @param array $extra Extra properties to add to the definition.
125  * @return array Definition (including referenced definitions).
126  */
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);
133  unset($def[null]);
134  }
135  foreach($def as $key => $value) if(substr($key,0,1) == '@'){
136  unset($def[$key]);
137  $def[substr($key,1)] = call_user_func($value);
138  }
139  return $def;
140  }
141  /**
142  * Definition value.
143  * @param string $table Table name.
144  * @param string $column Column name.
145  * @param string $key Key of value.
146  * @param mixed $default Default value if the key is not present in the definition.
147  */
148  public function value($table,$column,$key,$default = null){
149  return \Rsi\Record::get($this->column($table,$column),$key,$default);
150  }
151  /**
152  * Get the base (referenced) column.
153  * @param string $table
154  * @param string $column
155  * @return string Base column name.
156  */
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;
161  }
162  return null;
163  }
164  /**
165  * Convert a single value from database format to standard, internal format.
166  * @param string $type Column type.
167  * @param mixed $value Value in database format.
168  * @return mixed Value in standard, internal format.
169  */
170  public function convert($type,$value){
171  if($value !== null) switch($type){
172  case 'bool': return $value == $this->boolTrue;
173  case 'number': return $value + 0;
174  }
175  return $value;
176  }
177  /**
178  * Convert a single column value from database format to standard, internal format.
179  * @param string $column Column name.
180  * @param mixed $value Value in database format.
181  * @param array $tables Tables for the column.
182  * @return mixed Value in standard, internal format.
183  */
184  public function convertColumn($column,$value,$tables){
185  foreach($tables as $table) if($def = $this->column($table,$column))
186  return $this->convert($def['type'] ?? null,$value);
187  return $value;
188  }
189  /**
190  * Convert a record from database format to standard, internal format.
191  * @param array $record Record in database format.
192  * @param array $tables Tables for the column.
193  * @return array Record in standard, internal format.
194  */
195  public function convertRecord($record,$tables){
196  if($record) foreach($record as $column => &$value) $value = $this->convertColumn($column,$value,$tables);
197  unset($value);
198  return $record;
199  }
200  /**
201  * Format a value from standard, internal format to database format.
202  * @param string $type Column type.
203  * @param mixed $value Value in standard, internal format.
204  * @return mixed Value in database format.
205  */
206  public function format($type,$value){
207  if($value !== null) switch($type){
208  case 'bool': return $value ? $this->boolTrue : $this->boolFalse;
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;
211  }
212  return $value;
213  }
214  /**
215  * Convert a single column value from standard, internal format to database format.
216  * @param string $column Column name.
217  * @param mixed $value Value in standard, internal format.
218  * @param array $tables Tables for the column.
219  * @return mixed Value in database format.
220  */
221  public function formatColumn($column,$value,$tables){
222  foreach($tables as $table) if($def = $this->column($table,$column))
223  return $this->format($def['type'] ?? null,$value);
224  return $value;
225  }
226  /**
227  * Format a value from standard, internal format to database format.
228  * @param array $record Record in standard, internal format.
229  * @param array $tables Tables for the column.
230  * @return array Record in database format.
231  */
232  public function formatRecord($record,$tables){
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);
235  break;
236  }
237  unset($value);
238  return $record;
239  }
240 
241  protected function getDef(){
242  if($this->_def === null){
243  $this->_def = [];
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));
246  }
247  return $this->_def;
248  }
249 
250  public function __call($func_name,$params){
251  return $this->column($func_name,array_shift($params),array_shift($params));
252  }
253 
254 }
formatRecord($record, $tables)
Format a value from standard, internal format to database format.
Definition: Def.php:232
generateDatabaseStructure($table_trans=null)
Generate a definition file from the database structure.
Definition: Def.php:23
getDef()
Definition: Def.php:241
value($table, $column, $key, $default=null)
Definition value.
Definition: Def.php:148
$boolFalse
Definition: Def.php:13
__call($func_name, $params)
Definition: Def.php:250
ref($table, $column)
Get the base (referenced) column.
Definition: Def.php:157
convertColumn($column, $value, $tables)
Convert a single column value from database format to standard, internal format.
Definition: Def.php:184
key($table)
Primary key columns.
Definition: Def.php:115
formatColumn($column, $value, $tables)
Convert a single column value from standard, internal format to database format.
Definition: Def.php:221
$databaseStructureFilename
Definition: Def.php:9
column($table, $column, $extra=null)
Column definition.
Definition: Def.php:127
Basic component class.
Definition: Component.php:8
convertRecord($record, $tables)
Convert a record from database format to standard, internal format.
Definition: Def.php:195
$charLimit
Definition: Def.php:14
format($type, $value)
Format a value from standard, internal format to database format.
Definition: Def.php:206
convert($type, $value)
Convert a single value from database format to standard, internal format.
Definition: Def.php:170
table($table)
Table definition (all columns).
Definition: Def.php:107
component($name)
Get a component (local or default).
Definition: Component.php:80