6 * Generate DataObjects... and readers in the right places..
8 * note - we write to a temporary directory first...
13 require_once 'DB/DataObject/Generator.php';
16 /** basic thing now works...
18 * it needs a bit more intelligence to work out what to do...
21 * Basically we need to build up all the formats for each db column
23 * - overlay any mapping stuff.
25 * - overlay user defined settings
26 * = write it out to file..
29 $this->def['order'][$table][] = $t->name;
30 $this->def['readers'][$table][$t->name] = $reader;
31 $this->def['colmodels'][$table][$t->name] = $colmodel;
32 $this->def['forms'][$table][$t->name] = $form;
35 * readersDef[table.col]
43 class Pman_Builder_Generator extends DB_DataObject_Generator
54 * [tablename] => array( list of columns ones with '-' indicate lookup
56 * [tablename][colname] -> reader foramt
58 * [tablename][colname] => xtype / name etc...
60 * [tablename] => data for reader args (eg. id / total prop etc.)
62 * [tablename] => array of cols with types
64 * [tablename] -> array of cols
70 var $page = false; // page container when run from cli.
72 // dont do usual stuff!!!
74 var $tablekeys = array();
76 var $overwrite = array(); // default dont overwrite any of the files..
77 // array('master', 'corejs', 'corephp', 'index', 'Roo')
78 // and ('js-?????' where ??? is the table name) <- for all the generated js classes.
79 // we always overwrite the definition!!!
80 // set to array('all') to overwrite everything!!!
82 function start($cli=false, $mods='', $overwrite='')
85 $ff = HTML_Flexyframework::get();
87 //echo '<PRE>'; print_r($this->modtables); exit;
89 $options = &PEAR::getStaticProperty('DB_DataObject','options');
92 $proj = 'pman'; //ucfirst(basename($options['database']));
93 // we are going to generate all of the code into a temporay foldler..
94 $options['rootDir'] = ini_get('session.save_path').'/temp_'. $proj;
95 $options['cli'] = $cli;
96 $options['mods'] = empty($mods) ? array() : explode('/',$mods);
98 if (!file_exists($options['rootDir'])) {
99 mkdir($options['rootDir'], 0775, true);
102 $this->rootDir = $options['rootDir'];
103 $options['schema_location'] = $this->rootDir .'/'.$proj.'/DataObjects';
104 $options['class_location'] = $this->rootDir .'/'.$proj.'/DataObjects';
105 $options['require_prefix'] = $proj . '/DataObjects/';
106 $options['class_prefix'] = $proj . '_DataObjects_';
107 // print_r($this);exit;
112 $standard_database = $options['database'];
125 $this->scanModules();
126 require_once 'System.php';
127 $diff = System::which('diff');
128 // now for each of the directories copy/show diffs..
129 echo $cli ? '' : '<PRE>';
130 $flist = explode(',', $overwrite);
131 foreach($this->modtables as $m=>$ar) {
132 if ($options['database'] != $standard_database) {
133 $options['database'] = $standard_database ;
138 $options['database'] = $standard_database ;
139 if (isset($options['database_'. $m])) {
140 $options['database'] = $options['database_'. $m];
141 //var_dump($url);exit;
148 if (!empty($options['mods'] ) && !in_array($m, $options['mods'] )) {
151 foreach(scandir($options['rootDir'].'/'.$m) as $f) {
152 if (!strlen($f) || $f[0] == '.') {
156 $src = $options['rootDir']."/$m/$f";
157 $tg = $ff->page->rootDir."/Pman/$m/DataObjects/$f";
158 if (preg_match('/\.js$/', $f)) {
159 $tg = $ff->page->rootDir."/Pman/$m/$f";
162 if (!file_exists($tg) || !filesize($tg) ) {
164 if ($cli && in_array($f, $flist)) {
165 echo "COPY $src $tg" . ($cli ? "\n" : "<BR>");
169 echo "!!!!MISSING!!! $tg" . ($cli ? "\n" : "<BR>");
173 // always copy readers and ini file.= nope - not on live..
174 if ($cli && in_array($f, $flist)) {
176 //|| $f=='pman.ini' || preg_match('/\.js$/', $f))) {
177 echo "COPY $src $tg". ($cli ? "\n" : "<BR>");
183 $cmd = "$diff -u -w ". escapeshellarg($tg) . ' ' . escapeshellarg($src);
185 $out = array(); $ret = 0;
186 exec($cmd, $out, $ret);
187 if ($ret ==0) { // files match..
191 echo "\n" .implode( "\n" , $out) . "\n";
208 $options = &PEAR::getStaticProperty('DB_DataObject','options');
210 $ff = HTML_Flexyframework::get();
212 $url = parse_url($options['database']);
213 // hide stuff for web..
214 $cli = $options['cli'];
216 $url['pass'] = '*****';
217 $url['user'] = '*****';
218 $url['host'] = '*****';
223 require_once 'System.php';
224 $cat = System::which('cat');
225 $mysql = System::which('mysql');
226 print_r($options['mods'] );
227 foreach($this->modsql as $m => $fl)
229 if ($cli && isset($options['database_'. $m])) {
230 $url =parse_url($options['database_'.$m]);
233 $mysql_cmd = $mysql .
234 ' -h ' . $url['host'] .
235 ' -u' . escapeshellarg($url['user']) .
236 (!empty($url['pass']) ? ' -p' . escapeshellarg($url['pass']) : '') .
237 ' ' . basename($url['path']);
239 echo $mysql_cmd . "\n" ;
241 if (!empty($options['mods'] ) && !in_array($m, $options['mods'] )) {
246 $fn = $ff->page->rootDir. "/Pman/$m/DataObjects/$f";
247 $cmd = $cat . ' ' . escapeshellarg($fn) . " | $mysql_cmd -f ";
248 echo $cmd. ($cli ? "\n" : "<BR>\n");
260 * Scan the folders for DataObjects
261 * - Use the list of php files in DataObjects folders
262 * to determine which module owns which database table.
267 function scanModules()
270 $options = &PEAR::getStaticProperty('DB_DataObject','options');
271 if (isset($options['modtables'])) {
272 $this->modtables = $options['modtables'];
273 $this->modmap = $options['modmap'];
274 $this->modsql = $options['modsql'];
278 $ff = HTML_Flexyframework::get();
280 $top = $ff->page->rootDir .'/Pman';
281 $this->modtables = array();
282 $this->modmap = array();
283 $this->modmapsql = array();
285 foreach(scandir($top) as $m) {
289 !is_dir($top .'/'.$m) ||
290 !file_exists($top .'/'.$m.'/DataObjects')
294 $this->modtables[$m] = array();
295 $this->modsql[$m] = array();
296 foreach(scandir($top .'/'.$m.'/DataObjects') as $f) {
297 if (!strlen($f) || $m[0] == '.') {
300 if (preg_match('/\.sql$/', $f)) {
301 $this->modsql[$m][] = $f;
304 if (preg_match('/\.php$/', $f)) {
305 $tn = strtolower(preg_replace('/\.php$/', '', $f));
306 $this->modtables[$m][] = $tn;
307 $this->modmap[$tn] = $m;
312 $options['modtables'] = $this->modtables;
313 $options['modmap'] = $this->modmap;
314 $options['modsql'] = $this->modsql;
315 // print_r($options);
320 * this is run first, so picks up any missing dataobject files..
323 function generateDefinitions()
325 if (!$this->tables) {
326 $this->debug("-- NO TABLES -- \n");
329 if (!isset($this->modmap)) {
330 $this->scanModules();
332 $options = &PEAR::getStaticProperty('DB_DataObject','options');
333 $mods = $options['mods'];
335 $this->_newConfig = '';
336 foreach($this->tables as $this->table) {
338 $tn = strtolower($this->table);
339 //print_r($this->modmap);//[$tn]);//
340 if (!isset($this->modmap[$tn])) {
341 die("No existing DataObject file found for table {$this->table} \n".
342 "- create an empty file in the related Module/DataObjects directory
344 touch Pman/????/DataObjects/".ucfirst($this->table).".php
348 $mod = $this->modmap[$tn];
349 $inis[$mod] = isset($inis[$mod]) ? $inis[$mod] : '';
352 $this->_newConfig = '';
353 $this->_generateDefinitionsTable();
356 $inis[$mod] .= $this->_newConfig;
359 //echo '<PRE>';print_r($this->_inis); exit;
360 $options = PEAR::getStaticProperty('DB_DataObject','options');
362 $rd = $options['rootDir'];
363 foreach($inis as $m=>$ini) {
364 if (!empty($mods) && !in_array($m, $mods)) {
368 if (!file_exists($rd.'/'.$m)) {
369 mkdir($rd.'/'.$m, 0775, true);
371 $fname = '/pman.ini';
372 if (isset($options['database_'. $m])) {
373 $url = parse_url($options['database_'.$m]);
374 $fname = '/'. basename($url['path']).'.ini';
378 file_put_contents($rd.'/'.$m.$fname, $ini);
384 function generateClasses()
386 // print_R($this->modmap);
387 // die("generateClasses");
388 $options = &PEAR::getStaticProperty('DB_DataObject','options');
390 $ff = HTML_Flexyframework::get();
392 $rd = $options['rootDir'];
393 $mods = $options['mods'];
394 $this->_extends = 'DB_DataObject';
395 $this->_extendsFile = 'DB/DataObject.php';
396 $cli = $options['cli'];
398 foreach($this->tables as $this->table) {
399 $this->table = trim($this->table);
400 $tn = strtolower($this->table);
401 $mod = $this->modmap[$tn];
403 if (!empty($mods) && !in_array($mod, $mods)) {
408 $this->classname = 'Pman_'.$mod . '_DataObjects_'. ucfirst($this->table); // replace odd chars?
411 $outfilename = $rd.'/'.$mod.'/'. ucfirst($this->table).'.php';
412 $orig = $ff->page->rootDir .'/Pman/'.$mod.'/DataObjects/'. ucfirst($this->table).'.php';
415 // file_get_contents???
416 $oldcontents = file_get_contents($orig);
419 echo "GENERATE: " . $this->classname . ($cli ? "\n" : "<BR>");
421 $out = $this->_generateClassTable($oldcontents);
423 // get rid of static GET!!!
424 $out = preg_replace('/(\n|\r\n)\s*function staticGet[^\n]+(\n|\r\n)/s', '', $out);
425 $out = preg_replace('#/\* Static get \*/#s', '', $out);
428 // $this->debug( "writing $this->classname\n");
429 //$tmpname = tempnam(session_save_path(),'DataObject_');
430 file_put_contents($outfilename, $out);
437 // function generateDefinitions() { }
438 ////function generateForeignKeys() { }
439 // function generateClasses() { }
441 var $jsHeader = "//<script type=\"text/javascript\">\n";
443 function generateRoo()
446 $options = &PEAR::getStaticProperty('DB_DataObject','options');
447 $mods = $options['mods'];
448 $this->rootDir = $options['rootDir'];
449 $this->overwrite = true;
452 require_once 'Pman/Builder/Generator/JSON.php';
454 $ret = '//<script type="text/javascript">'."\n";
457 foreach($this->tables as $this->table) {
458 $this->_generateData($this->table);
462 //echo '<PRE>';print_R($this->def);
463 $this->parseConfig();
468 $ds = $this->_database;
469 $ds = ucfirst($this->_database);
471 // we always write these files....
472 foreach($this->modtables as $m=>$ts) {
473 if (!empty($mods) && !in_array($m, $mods)) {
477 file_put_contents($this->rootDir."/$m/$m.readers.js", $this->_generateReaders($m));
488 function _generateReaders($m)
491 $udb = ucfirst($this->_database);
492 $ret = $this->jsHeader;
493 $j = new Pman_Builder_Generator_JSON();
496 $j2 = new Pman_Builder_Generator_JSON(array('crlf' => '', 'tab' => ''));
499 foreach($this->tables as $this->table) {
502 if ($this->modmap[strtolower($this->table)] != $m) {
506 $utable = ucfirst($this->table);
508 $r = $this->def['readers'][$this->table];
509 foreach($r as $k=>$tab) {
510 if ($tab['type'] == 'string') {
511 $r[$k] = $tab['name'];
514 $this->readersArgs[$this->table]['xtype'] = 'JsonReader';
515 $ret.="\n$udb.Readers.$utable = ";
516 $x = $j->encodeUnsafe($this->readersArgs[$this->table]);
517 $ret .= trim(substr($x, 0, -1)) . ",\n"; // strip of trailing ;};
518 $ret .= $j->tab . "fields : [\n". $j->tab.$j->tab;
521 $ar[] = $j2->encodeUnsafe($xr);
523 $ret .= implode(",\n". $j->tab.$j->tab, $ar);
524 $ret .= "\n". $j->tab . "]\n};\n";
532 * Generic covert databse data to readers etc..
534 * $x = DB_DataObject::factory('anytable');
535 * $def = $x->getDatabaseConnection()->tableInfo($table);
536 * $mydata = Generator::tableToData($table, $def);
538 * returns tablekey, args(for reader header), order, readers, colmodels, forms.
544 function tableToData($table, $def)
550 'tablekey' => '', //$this->tablekeys[$this->table]
551 'args' => array( // was retiun..
553 'totalProperty' => 'total',
554 'id' => 'id', // primary key!!?
557 'readers' => array(),
558 'colmodels' => array(),
564 $colmodels = array();
566 $utable = ucfirst($table);
567 //$this->tablekeys[$this->table] = '';
569 foreach($def as $t) {
578 'id' => str_replace('_', '-', strtolower($utable.'-'.$t->name)),
579 'header' => ucfirst($t->name), // get from somewhere?!!?!?
580 'dataIndex' => $t->name,
587 'fieldLabel' => str_replace(array('_id', '_'), array('', ' '), ucfirst($t->name)),
589 'allowBlank' => preg_match('/not_null/i', $t->flags) ? false : true,
590 'qtip' => 'Enter '. $t->name ,
594 switch (strtoupper($t->type)) {
597 case 'INT2': // postgres
598 case 'INT4': // postgres
599 case 'INT8': // postgres
600 case 'SERIAL4': // postgres
601 case 'SERIAL8': // postgres
607 $colmodel['width'] = 100;
608 $reader['type'] = 'int';
609 $felement['xtype'] = 'NumberField';
610 $felement['allowDecimals'] = false;
612 $reader['type'] = 'boolean';
618 case 'DOUBLE PRECISION': // double precision (firebird)
620 case 'FLOAT4': // real (postgres)
621 case 'FLOAT8': // double precision (postgres)
623 case 'MONEY': // mssql and maybe others
625 case 'NUMBER': // oci8
626 $reader['type'] = 'float'; // should really by FLOAT!!! / MONEY...
628 $colmodel['width'] = 100;
629 $felement['xtype'] = 'NumberField';
633 $reader['type'] = 'int';
634 $felement['allowDecimals'] = false;
635 $felement['xtype'] = 'NumberField';
641 $colmodel['width'] = 50;
642 $reader['type'] = 'boolean';
643 $felement['xtype'] = 'CheckBox';
644 // postgres needs to quote '0'
655 case 'SET': // not really but oh well
657 case 'POINT': // mysql geometry stuff - not really string - but will do..
659 case 'TIMESTAMPTZ': // postgres
660 case 'BPCHAR': // postgres
661 case 'INTERVAL': // postgres (eg. '12 days')
663 case 'CIDR': // postgres IP net spec
664 case 'INET': // postgres IP
665 case 'MACADDR': // postgress network Mac address.
667 case 'INTEGER[]': // postgres type
668 case 'BOOLEAN[]': // postgres type
670 $colmodel['width'] = isset($t->len) ? max(50, min(floor($t->len * 10), 300)) : 10;
671 // editor - $colmodel['allowBlank'] = preg_match('/not_null/i', $t->flags) ? false, true;
672 $reader['type'] = 'string';
673 $felement['xtype'] = 'TextField';
679 $colmodel['width'] = 300;
680 $reader['type'] = 'string';
681 $felement['xtype'] = 'TextArea'; // or HtmlEditor
682 $felement['height'] = 100;
687 $colmodel['width'] = 100;
688 ///$colmodel['renderer'] =
690 $reader['type'] = 'date';
691 $reader['dateFormat'] = 'Y-m-d';
692 $felement['xtype'] = 'DateField';
694 $felement['altFormats'] = 'Y-m-d|d/m/Y';
695 $felement['format'] = 'd/m/Y';
696 $felement['hiddenFormat'] = 'Y-m-d'; // not supported ATM
701 $colmodel['width'] = 100;
702 ///$colmodel['renderer'] =
703 $reader['type'] = 'string';
704 $felement['xtype'] = 'TextField';
709 $colmodel['width'] = 100;
710 ///$colmodel['renderer'] =
711 $reader['type'] = 'date';
712 $reader['dateFormat'] = 'Y-m-d H:i:s';
713 $felement['xtype'] = 'TextField';
714 $felement['readOnly'] = 'true';
720 case 'TIMESTAMP': // do other databases use this???
721 $colmodel['width'] = 100;
722 ///$colmodel['renderer'] =
723 $reader['type'] = 'date';
724 $reader['dateFormat'] = 'YmdHis';
725 $felement['xtype'] = 'TextField';
726 $felement['readOnly'] = 'true';
730 case 'BLOB': /// these should really be ignored!!!???
735 case 'CLOB': // oracle character lob support
737 case 'BYTEA': // postgres blob support..
738 $colmodel['width'] = 300;
739 $reader['type'] = 'string';
740 $felement['xtype'] = 'TextArea'; // or HtmlEditor
741 $felement['height'] = 100;
745 echo "*****************************************************************\n".
746 "** WARNING UNKNOWN TYPE **\n".
747 "** Found column '{$t->name}', of type '{$t->type}' **\n".
748 "** Please submit a bug, describe what type you expect this **\n".
749 "** column to be **\n".
750 "** ---------POSSIBLE FIX / WORKAROUND -------------------------**\n".
751 "** Try using MDB2 as the backend - eg set the config option **\n".
752 "** db_driver = MDB2 **\n".
753 "*****************************************************************\n";
758 if (preg_match('/(auto_increment|nextval\()/i',rawurldecode($t->flags))
759 || (isset($t->autoincrement) && ($t->autoincrement === true))) {
761 if (empty($ret['tablekeys'])) {
762 $ret['tablekeys'] = $t->name;
771 // form inherits from users' colmodel width..
772 $felement['width'] = $colmodel['width'];
774 //$readers[] = $reader;
775 //$colmodels[] = $colmodel;
776 if ($ret['tablekey'] == $t->name) {
778 $felement['xtype'] = 'Hidden';
779 unset($felement['allowBlank']);
780 unset($felement['fieldLabel']);
786 // only put id in the key col.
787 unset($colmodel['id']);
789 // hidden elemetns do not need any display components..
790 if ($felement['xtype'] == 'Hidden') {
791 foreach($felement as $k=>$v) {
792 if (!in_array($k , array('name', 'xtype'))) {
793 unset($felement[$k]);
799 // $form[] = $felement;
802 $ret['order'][] = $t->name;
803 $ret['readers'][$t->name] = $reader;
804 $ret['colmodels'][$t->name] = $colmodel;
805 $ret['forms'][$t->name] = $felement;
809 //$ret['args'] = $args;
815 function _generateData($table)
820 'totalProperty' => 'total',
821 'id' => 'id', // primary key!!?
824 $d = $this->tableToData($this->_definitions[$table]);
826 $this->tablekeys[$this->table] = $d['tablekey'];
828 // $form[] = $felement;
831 $this->def['order'][$table] = $d['order'];
832 $this->def['readers'][$table]= $d['readers'];
833 $this->def['colmodels'][$table] = $d['colmodes'];
834 $this->def['forms'][$table] = $d['forms'];
836 $this->readersArgs[$table] = $d['args'];
845 function parseConfig()
847 $options = &PEAR::getStaticProperty('DB_DataObject','options');
849 if (isset($options['modtables'])) {
850 $this->modtables = $options['modtables'];
851 $this->modmap = $options['modmap'];
852 $this->modsql = $options['modsql'];
855 $ff = HTML_Flexyframework::get();
856 $dirs = array($ff->page->rootDir.'/Pman/DataObjects'); // not used anymore!
857 foreach($this->modtables as $m=>$ts) {
858 $dirs[] = $ff->page->rootDir.'/Pman/'.$m.'/DataObjects';
860 $ini = array('database__render' => array());
861 foreach($dirs as $d) {
862 if (!file_exists($d.'/pman.links.ini')) {
865 $in = parse_ini_file($d.'/pman.links.ini',true);
867 if (isset($in['database__render'])) {
868 $r = $in['database__render'];
869 unset($in['database__render']);
871 $ini = array_merge($ini, $in);
872 $ini['database__render'] = array_merge($ini['database__render'] , $r);
874 //echo '<PRE>';print_R($ini);//exit;
877 if (!isset($ini['database__render'])) {
878 die("database__render not available in links files.");
881 $this->mapcols = array();
882 foreach($ini as $tab=>$conf) {
883 if ($tab == 'database__render') {
886 $this->mergeConfig($tab,$conf,$ini['database__render']);
889 $this->renderMap = $ini['database__render'];
891 function mergeConfig($table, $conf, $render)
893 $this->mapcols[$table] = array();
894 $options = &PEAR::getStaticProperty('DB_DataObject','options');
895 if (isset($options['modtables'])) {
896 $this->modtables = $options['modtables'];
897 $this->modmap = $options['modmap'];
898 $this->modsql = $options['modsql'];
902 foreach($conf as $ocol=>$info) {
903 // format col => showval..
904 //list($rtc, $rshow) = explode(':', $info);
905 list($tab,$col) = explode(':', $info);
907 $rshow = $render[$tab];
909 $this->mapcols[$table][$ocol] = array('table'=>$tab, 'col' => $col);
914 //- just add an extra line..
915 if (!isset($this->def['readers'][$tab][$rshow])){
916 echo "WARNING in links.ini TABLE $tab does not have renderer $rshow <BR>\n";
920 // for the readers.. we need to merge all the columns in the left to the right...
923 // ocol => column in table
924 // tab => remote table
925 // col => right col linked to...
927 $rdef = $this->_definitions[$tab];
930 foreach($rdef as $t) {
931 //copy typedata from old coll
932 $this->def['readers'][$table][$ocol.'-'. $t->name] = $this->def['readers'][$tab][$t->name];
933 $this->def['readers'][$table][$ocol.'-'. $t->name]['name'] = $ocol.'_'. $t->name;
940 // remove the def column from the id one..
941 if (isset($this->def['colmodels'][$table][$ocol])) {
942 unset($this->def['colmodels'][$table][$ocol]);
944 $this->def['colmodels'][$table][$ocol.'-'. $rshow] =
945 $this->def['colmodels'][$tab][$rshow];
947 // change the header name (merge of two..)
948 list($colname,) = explode('_',$ocol,2);
950 $this->def['colmodels'][$table][$ocol.'-'. $rshow]['dataIndex'] = $ocol.'_'. $rshow;
951 $this->def['colmodels'][$table][$ocol.'-'. $rshow]['id'] = $ocol.'-'. $rshow;
953 $this->def['colmodels'][$table][$ocol.'-'. $rshow]['header'] = ucwords($colname . ' ' .
954 $this->def['colmodels'][$tab][$rshow]['header']);
956 // last of all add replace the old $col, with
957 $p = array_search($ocol, empty($this->def['order'][$table]) ? array() : $this->def['order'][$table]);
958 $this->def['order'][$table][$p] = $ocol.'-'. $rshow;
959 $this->def['order'][$table][] = $ocol;
961 // --- now for forms!!!!
966 //print_r( $this->def['readers'][$table]);
967 // print_r( $this->def['colmodels'][$table]);
968 //print_r($this->def['readers'][$table]); exit;
973 function writeFileEx($n, $f, $str)
975 if (file_exists($f)) {
976 // all - will not overwrite stuff.. (only being specific willl)
977 if (!in_array($n, $this->overwrite)) {
978 $this->writeFile($f.'.generated',$str);
982 $this->writeFile($f,$str);
986 function writeFile($f, $str)
988 require_once 'System.php';
989 System::mkdir(array('-p', dirname($f)));
992 file_put_contents($f, $str);