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 $user = posix_getpwuid(posix_getuid());
96 $options['rootDir'] = ini_get('session.save_path').'/temp_'. $proj.'_'. $user['name'];
97 $options['cli'] = $cli;
98 $options['mods'] = empty($mods) ? array() : explode('/',$mods);
100 if (!file_exists($options['rootDir'])) {
101 mkdir($options['rootDir'], 0775, true);
104 $this->rootDir = $options['rootDir'];
105 $options['schema_location'] = $this->rootDir .'/'.$proj.'/DataObjects';
106 $options['class_location'] = $this->rootDir .'/'.$proj.'/DataObjects';
107 $options['require_prefix'] = $proj . '/DataObjects/';
108 $options['class_prefix'] = $proj . '_DataObjects_';
109 // print_r($this);exit;
114 $standard_database = $options['database'];
126 $this->scanModules();
127 require_once 'System.php';
128 $diff = System::which('diff');
129 // now for each of the directories copy/show diffs..
130 echo $cli ? '' : '<PRE>';
131 $flist = explode(',', $overwrite);
132 foreach($this->modtables as $m=>$ar) {
133 if ($options['database'] != $standard_database) {
134 $options['database'] = $standard_database ;
139 $options['database'] = $standard_database ;
140 if (isset($options['database_'. $m])) {
141 $options['database'] = $options['database_'. $m];
142 //var_dump($url);exit;
149 if (!empty($options['mods'] ) && !in_array($m, $options['mods'] )) {
152 foreach(scandir($options['rootDir'].'/'.$m) as $f) {
153 if (!strlen($f) || $f[0] == '.') {
157 $src = $options['rootDir']."/$m/$f";
158 $tg = $ff->page->rootDir."/Pman/$m/DataObjects/$f";
159 if (preg_match('/\.js$/', $f)) {
160 $tg = $ff->page->rootDir."/Pman/$m/$f";
163 if (!file_exists($tg) || !filesize($tg) ) {
165 if ($cli && in_array($f, $flist)) {
166 echo "COPY $src $tg" . ($cli ? "\n" : "<BR>");
170 echo "!!!!MISSING!!! $tg" . ($cli ? "\n" : "<BR>");
174 // always copy readers and ini file.= nope - not on live..
175 if ($cli && in_array($f, $flist)) {
177 //|| $f=='pman.ini' || preg_match('/\.js$/', $f))) {
178 echo "COPY $src $tg". ($cli ? "\n" : "<BR>");
184 $cmd = "$diff -u -w ". escapeshellarg($tg) . ' ' . escapeshellarg($src);
186 $out = array(); $ret = 0;
187 exec($cmd, $out, $ret);
188 if ($ret ==0) { // files match..
192 echo "\n" .implode( "\n" , $out) . "\n";
209 $options = &PEAR::getStaticProperty('DB_DataObject','options');
211 $ff = HTML_Flexyframework::get();
213 $url = parse_url($options['database']);
214 // hide stuff for web..
215 $cli = $options['cli'];
217 $url['pass'] = '*****';
218 $url['user'] = '*****';
219 $url['host'] = '*****';
224 require_once 'System.php';
225 $cat = System::which('cat');
226 $mysql = System::which('mysql');
228 //print_r($options['mods'] );
229 foreach($this->modsql as $m => $fl)
231 if ($cli && isset($options['database_'. $m])) {
232 $url =parse_url($options['database_'.$m]);
235 $mysql_cmd = $mysql .
236 ' -h ' . $url['host'] .
237 ' -u' . escapeshellarg($url['user']) .
238 (!empty($url['pass']) ? ' -p' . escapeshellarg($url['pass']) : '') .
239 ' ' . basename($url['path']);
241 echo $mysql_cmd . "\n" ;
243 if (!empty($options['mods'] ) && !in_array($m, $options['mods'] )) {
248 $fn = $ff->page->rootDir. "/Pman/$m/DataObjects/$f";
249 $cmd = $cat . ' ' . escapeshellarg($fn) . " | $mysql_cmd -f ";
250 echo $cmd. ($cli ? "\n" : "<BR>\n");
262 * Scan the folders for DataObjects
263 * - Use the list of php files in DataObjects folders
264 * to determine which module owns which database table.
269 function scanModules()
272 $options = &PEAR::getStaticProperty('DB_DataObject','options');
273 if (isset($options['modtables'])) {
274 $this->modtables = $options['modtables'];
275 $this->modmap = $options['modmap'];
276 $this->modsql = $options['modsql'];
280 $ff = HTML_Flexyframework::get();
282 $top = $ff->page->rootDir .'/Pman';
283 $this->modtables = array();
284 $this->modmap = array();
285 $this->modmapsql = array();
287 foreach(scandir($top) as $m) {
291 !is_dir($top .'/'.$m) ||
292 !file_exists($top .'/'.$m.'/DataObjects')
296 $this->modtables[$m] = array();
297 $this->modsql[$m] = array();
298 foreach(scandir($top .'/'.$m.'/DataObjects') as $f) {
299 if (!strlen($f) || $m[0] == '.') {
302 if (preg_match('/\.sql$/', $f)) {
303 $this->modsql[$m][] = $f;
306 if (preg_match('/\.php$/', $f)) {
307 $tn = strtolower(preg_replace('/\.php$/', '', $f));
308 $this->modtables[$m][] = $tn;
309 $this->modmap[$tn] = $m;
314 $options['modtables'] = $this->modtables;
315 $options['modmap'] = $this->modmap;
316 $options['modsql'] = $this->modsql;
317 // print_r($options);
322 * this is run first, so picks up any missing dataobject files..
325 function generateDefinitions()
327 if (!$this->tables) {
328 $this->debug("-- NO TABLES -- \n");
331 if (!isset($this->modmap)) {
332 $this->scanModules();
334 $options = &PEAR::getStaticProperty('DB_DataObject','options');
335 $builder_options = PEAR::getStaticProperty('Pman_Builder','options');
336 $ignore = empty($builder_options['skip_tables']) ? array() : $builder_options['skip_tables'];
338 $mods = $options['mods'];
340 $this->_newConfig = '';
341 foreach($this->tables as $this->table) {
343 $tn = strtolower($this->table);
344 //print_r($this->modmap);//[$tn]);//
348 if (!isset($this->modmap[$tn])) {
350 if (in_array($this->table, $ignore)) {
355 die("No existing DataObject file found for table {$this->table}
357 - either add it to Pman_Builder[skip_tables] or\n
358 - create an empty file in the related Module/DataObjects directory
360 touch Pman/????/DataObjects/".ucfirst($this->table).".php
365 $mod = $this->modmap[$tn];
366 $inis[$mod] = isset($inis[$mod]) ? $inis[$mod] : '';
369 $this->_newConfig = '';
370 $this->_generateDefinitionsTable();
373 $inis[$mod] .= $this->_newConfig;
376 //echo '<PRE>';print_r($this->_inis); exit;
377 $options = PEAR::getStaticProperty('DB_DataObject','options');
379 $rd = $options['rootDir'];
380 foreach($inis as $m=>$ini) {
381 if (!empty($mods) && !in_array($m, $mods)) {
385 if (!file_exists($rd.'/'.$m)) {
386 mkdir($rd.'/'.$m, 0775, true);
388 $fname = '/pman.ini';
389 if (isset($options['database_'. $m])) {
390 $url = parse_url($options['database_'.$m]);
391 $fname = '/'. basename($url['path']).'.ini';
395 file_put_contents($rd.'/'.$m.$fname, $ini);
401 function generateClasses()
403 // print_R($this->modmap);
404 // die("generateClasses");
405 $options = &PEAR::getStaticProperty('DB_DataObject','options');
407 $ff = HTML_Flexyframework::get();
409 $rd = $options['rootDir'];
410 $mods = $options['mods'];
411 $this->_extends = 'DB_DataObject';
412 $this->_extendsFile = 'DB/DataObject.php';
413 $cli = $options['cli'];
415 foreach($this->tables as $this->table) {
416 $this->table = trim($this->table);
417 $tn = strtolower($this->table);
418 $mod = $this->modmap[$tn];
420 if (!empty($mods) && !in_array($mod, $mods)) {
425 $this->classname = 'Pman_'.$mod . '_DataObjects_'. ucfirst($this->table); // replace odd chars?
428 $outfilename = $rd.'/'.$mod.'/'. ucfirst($this->table).'.php';
429 $orig = $ff->page->rootDir .'/Pman/'.$mod.'/DataObjects/'. ucfirst($this->table).'.php';
432 // file_get_contents???
433 $oldcontents = file_get_contents($orig);
436 echo "GENERATE: " . $this->classname . ($cli ? "\n" : "<BR>");
438 $out = $this->_generateClassTable($oldcontents);
440 // get rid of static GET!!!
441 $out = preg_replace('/(\n|\r\n)\s*function staticGet[^\n]+(\n|\r\n)/s', '', $out);
442 $out = preg_replace('#/\* Static get \*/#s', '', $out);
445 // $this->debug( "writing $this->classname\n");
446 //$tmpname = tempnam(session_save_path(),'DataObject_');
447 file_put_contents($outfilename, $out);
454 // function generateDefinitions() { }
455 ////function generateForeignKeys() { }
456 // function generateClasses() { }
458 var $jsHeader = "//<script type=\"text/javascript\">\n";
461 * generate the reader tables..
462 * I'm not sure if this is needed any more, as our desktop builder
463 * generates that data anyway.
466 function generateRoo()
469 $options = &PEAR::getStaticProperty('DB_DataObject','options');
470 $mods = $options['mods'];
471 $this->rootDir = $options['rootDir'];
472 $this->overwrite = true;
475 require_once 'Pman/Builder/Generator/JSON.php';
477 $ret = '//<script type="text/javascript">'."\n";
480 foreach($this->tables as $this->table) {
481 $this->_generateData($this->table);
485 //echo '<PRE>';print_R($this->def);
486 $this->parseConfig();
491 $ds = $this->_database;
492 $ds = ucfirst($this->_database);
494 // we always write these files....
495 foreach($this->modtables as $m=>$ts) {
496 if (!empty($mods) && !in_array($m, $mods)) {
500 file_put_contents($this->rootDir."/$m/$m.readers.js", $this->_generateReaders($m));
511 function _generateReaders($m)
513 $ff = HTML_FlexyFramework::get();
515 $ret = $this->jsHeader;
516 $j = new Pman_Builder_Generator_JSON();
519 $j2 = new Pman_Builder_Generator_JSON(array('crlf' => '', 'tab' => ''));
522 foreach($this->tables as $this->table) {
525 if ($this->modmap[strtolower($this->table)] != $m) {
529 $utable = ucfirst($this->table);
531 $r = $this->def['readers'][$this->table];
532 foreach($r as $k=>$tab) {
533 if ($tab['type'] == 'string') {
534 $r[$k] = $tab['name'];
537 $this->readersArgs[$this->table]['xtype'] = 'JsonReader';
538 $ret.="\n{$ff->project}.Readers.$utable = ";
539 $x = $j->encodeUnsafe($this->readersArgs[$this->table]);
540 $ret .= trim(substr($x, 0, -1)) . ",\n"; // strip of trailing ;};
541 $ret .= $j->tab . "fields : [\n". $j->tab.$j->tab;
544 $ar[] = $j2->encodeUnsafe($xr);
546 $ret .= implode(",\n". $j->tab.$j->tab, $ar);
547 $ret .= "\n". $j->tab . "]\n};\n";
555 * Generic covert databse data to readers etc..
557 * $x = DB_DataObject::factory('anytable');
558 * $def = $x->getDatabaseConnection()->tableInfo($table);
559 * $mydata = Generator::tableToData($table, $def);
561 * returns tablekey, args(for reader header), order, readers, colmodels, forms.
567 function tableToData($table, $def)
573 'tablekey' => '', //$this->tablekeys[$this->table]
574 'args' => array( // was retiun..
576 'totalProperty' => 'total',
577 'id' => 'id', // primary key!!?
580 'readers' => array(),
581 'colmodels' => array(),
587 $colmodels = array();
589 $utable = ucfirst($table);
590 //$this->tablekeys[$this->table] = '';
592 foreach($def as $t) {
601 'id' => str_replace('_', '-', strtolower($utable.'-'.$t->name)),
602 'header' => ucfirst($t->name), // get from somewhere?!!?!?
603 'dataIndex' => $t->name,
610 'fieldLabel' => str_replace(array('_id', '_'), array('', ' '), ucfirst($t->name)),
612 'allowBlank' => preg_match('/not_null/i', $t->flags) ? false : true,
613 'qtip' => 'Enter '. $t->name ,
617 switch (strtoupper($t->type)) {
620 case 'INT2': // postgres
621 case 'INT4': // postgres
622 case 'INT8': // postgres
623 case 'SERIAL4': // postgres
624 case 'SERIAL8': // postgres
630 $colmodel['width'] = 100;
631 $reader['type'] = 'int';
632 $felement['xtype'] = 'NumberField';
633 $felement['allowDecimals'] = false;
635 $reader['type'] = 'boolean';
641 case 'DOUBLE PRECISION': // double precision (firebird)
643 case 'FLOAT4': // real (postgres)
644 case 'FLOAT8': // double precision (postgres)
646 case 'MONEY': // mssql and maybe others
648 case 'NUMBER': // oci8
649 $reader['type'] = 'float'; // should really by FLOAT!!! / MONEY...
651 $colmodel['width'] = 100;
652 $felement['xtype'] = 'NumberField';
656 $reader['type'] = 'int';
657 $felement['allowDecimals'] = false;
658 $felement['xtype'] = 'NumberField';
664 $colmodel['width'] = 50;
665 $reader['type'] = 'boolean';
666 $felement['xtype'] = 'CheckBox';
667 // postgres needs to quote '0'
678 case 'SET': // not really but oh well
680 case 'POINT': // mysql geometry stuff - not really string - but will do..
682 case 'TIMESTAMPTZ': // postgres
683 case 'BPCHAR': // postgres
684 case 'INTERVAL': // postgres (eg. '12 days')
686 case 'CIDR': // postgres IP net spec
687 case 'INET': // postgres IP
688 case 'MACADDR': // postgress network Mac address.
690 case 'INTEGER[]': // postgres type
691 case 'BOOLEAN[]': // postgres type
693 $colmodel['width'] = isset($t->len) ? max(50, min(floor($t->len * 10), 300)) : 10;
694 // editor - $colmodel['allowBlank'] = preg_match('/not_null/i', $t->flags) ? false, true;
695 $reader['type'] = 'string';
696 $felement['xtype'] = 'TextField';
702 $colmodel['width'] = 300;
703 $reader['type'] = 'string';
704 $felement['xtype'] = 'TextArea'; // or HtmlEditor
705 $felement['height'] = 100;
710 $colmodel['width'] = 100;
711 ///$colmodel['renderer'] =
713 $reader['type'] = 'date';
714 $reader['dateFormat'] = 'Y-m-d';
715 $felement['xtype'] = 'DateField';
717 $felement['altFormats'] = 'Y-m-d|d/m/Y';
718 $felement['format'] = 'd/m/Y';
719 $felement['hiddenFormat'] = 'Y-m-d'; // not supported ATM
724 $colmodel['width'] = 100;
725 ///$colmodel['renderer'] =
726 $reader['type'] = 'string';
727 $felement['xtype'] = 'TextField';
732 $colmodel['width'] = 100;
733 ///$colmodel['renderer'] =
734 $reader['type'] = 'date';
735 $reader['dateFormat'] = 'Y-m-d H:i:s';
736 $felement['xtype'] = 'TextField';
737 $felement['readOnly'] = 'true';
743 case 'TIMESTAMP': // do other databases use this???
744 $colmodel['width'] = 100;
745 ///$colmodel['renderer'] =
746 $reader['type'] = 'date';
747 $reader['dateFormat'] = 'YmdHis';
748 $felement['xtype'] = 'TextField';
749 $felement['readOnly'] = 'true';
753 case 'BLOB': /// these should really be ignored!!!???
758 case 'CLOB': // oracle character lob support
760 case 'BYTEA': // postgres blob support..
761 $colmodel['width'] = 300;
762 $reader['type'] = 'string';
763 $felement['xtype'] = 'TextArea'; // or HtmlEditor
764 $felement['height'] = 100;
768 echo "*****************************************************************\n".
769 "** WARNING UNKNOWN TYPE **\n".
770 "** Found column '{$t->name}', of type '{$t->type}' **\n".
771 "** Please submit a bug, describe what type you expect this **\n".
772 "** column to be **\n".
773 "** ---------POSSIBLE FIX / WORKAROUND -------------------------**\n".
774 "** Try using MDB2 as the backend - eg set the config option **\n".
775 "** db_driver = MDB2 **\n".
776 "*****************************************************************\n";
781 if (preg_match('/(auto_increment|nextval\()/i',rawurldecode($t->flags))
782 || (isset($t->autoincrement) && ($t->autoincrement === true))) {
784 if (empty($ret['tablekeys'])) {
785 $ret['tablekeys'] = $t->name;
794 // form inherits from users' colmodel width..
795 $felement['width'] = $colmodel['width'];
797 //$readers[] = $reader;
798 //$colmodels[] = $colmodel;
799 if ($ret['tablekey'] == $t->name) {
801 $felement['xtype'] = 'Hidden';
802 unset($felement['allowBlank']);
803 unset($felement['fieldLabel']);
809 // only put id in the key col.
810 unset($colmodel['id']);
812 // hidden elemetns do not need any display components..
813 if ($felement['xtype'] == 'Hidden') {
814 foreach($felement as $k=>$v) {
815 if (!in_array($k , array('name', 'xtype'))) {
816 unset($felement[$k]);
822 // $form[] = $felement;
825 $ret['order'][] = $t->name;
826 $ret['readers'][$t->name] = $reader;
827 $ret['colmodels'][$t->name] = $colmodel;
828 $ret['forms'][$t->name] = $felement;
832 //$ret['args'] = $args;
838 function _generateData($table)
843 'totalProperty' => 'total',
844 'id' => 'id', // primary key!!?
847 $d = $this->tableToData($table, $this->_definitions[$table]);
849 $this->tablekeys[$this->table] = $d['tablekey'];
851 // $form[] = $felement;
854 $this->def['order'][$table] = $d['order'];
855 $this->def['readers'][$table]= $d['readers'];
856 $this->def['colmodels'][$table] = $d['colmodels'];
857 $this->def['forms'][$table] = $d['forms'];
859 $this->readersArgs[$table] = $d['args'];
868 function parseConfig()
870 $options = &PEAR::getStaticProperty('DB_DataObject','options');
872 if (isset($options['modtables'])) {
873 $this->modtables = $options['modtables'];
874 $this->modmap = $options['modmap'];
875 $this->modsql = $options['modsql'];
878 $ff = HTML_Flexyframework::get();
879 $dirs = array($ff->page->rootDir.'/Pman/DataObjects'); // not used anymore!
880 foreach($this->modtables as $m=>$ts) {
881 $dirs[] = $ff->page->rootDir.'/Pman/'.$m.'/DataObjects';
883 $ini = array('database__render' => array());
884 foreach($dirs as $d) {
885 if (!file_exists($d.'/pman.links.ini')) {
888 $in = parse_ini_file($d.'/pman.links.ini',true);
890 if (isset($in['database__render'])) {
891 $r = $in['database__render'];
892 unset($in['database__render']);
894 $ini = array_merge($ini, $in);
895 $ini['database__render'] = array_merge($ini['database__render'] , $r);
897 //echo '<PRE>';print_R($ini);//exit;
900 if (!isset($ini['database__render'])) {
901 die("database__render not available in links files.");
904 $this->mapcols = array();
905 foreach($ini as $tab=>$conf) {
906 if ($tab == 'database__render') {
909 $this->mergeConfig($tab,$conf,$ini['database__render']);
912 $this->renderMap = $ini['database__render'];
914 function mergeConfig($table, $conf, $render)
916 $this->mapcols[$table] = array();
917 $options = &PEAR::getStaticProperty('DB_DataObject','options');
918 if (isset($options['modtables'])) {
919 $this->modtables = $options['modtables'];
920 $this->modmap = $options['modmap'];
921 $this->modsql = $options['modsql'];
925 foreach($conf as $ocol=>$info) {
926 // format col => showval..
927 //list($rtc, $rshow) = explode(':', $info);
928 list($tab,$col) = explode(':', $info);
930 $rshow = $render[$tab];
932 $this->mapcols[$table][$ocol] = array('table'=>$tab, 'col' => $col);
937 //- just add an extra line..
938 if (!isset($this->def['readers'][$tab][$rshow])){
939 echo "WARNING in links.ini TABLE $tab does not have renderer $rshow <BR>\n";
943 // for the readers.. we need to merge all the columns in the left to the right...
946 // ocol => column in table
947 // tab => remote table
948 // col => right col linked to...
950 $rdef = $this->_definitions[$tab];
953 foreach($rdef as $t) {
954 //copy typedata from old coll
955 $this->def['readers'][$table][$ocol.'-'. $t->name] = $this->def['readers'][$tab][$t->name];
956 $this->def['readers'][$table][$ocol.'-'. $t->name]['name'] = $ocol.'_'. $t->name;
963 // remove the def column from the id one..
964 if (isset($this->def['colmodels'][$table][$ocol])) {
965 unset($this->def['colmodels'][$table][$ocol]);
967 $this->def['colmodels'][$table][$ocol.'-'. $rshow] =
968 $this->def['colmodels'][$tab][$rshow];
970 // change the header name (merge of two..)
971 list($colname,) = explode('_',$ocol,2);
973 $this->def['colmodels'][$table][$ocol.'-'. $rshow]['dataIndex'] = $ocol.'_'. $rshow;
974 $this->def['colmodels'][$table][$ocol.'-'. $rshow]['id'] = $ocol.'-'. $rshow;
976 $this->def['colmodels'][$table][$ocol.'-'. $rshow]['header'] = ucwords($colname . ' ' .
977 $this->def['colmodels'][$tab][$rshow]['header']);
979 // last of all add replace the old $col, with
980 $p = array_search($ocol, empty($this->def['order'][$table]) ? array() : $this->def['order'][$table]);
981 $this->def['order'][$table][$p] = $ocol.'-'. $rshow;
982 $this->def['order'][$table][] = $ocol;
984 // --- now for forms!!!!
989 //print_r( $this->def['readers'][$table]);
990 // print_r( $this->def['colmodels'][$table]);
991 //print_r($this->def['readers'][$table]); exit;
996 function writeFileEx($n, $f, $str)
998 if (file_exists($f)) {
999 // all - will not overwrite stuff.. (only being specific willl)
1000 if (!in_array($n, $this->overwrite)) {
1001 $this->writeFile($f.'.generated',$str);
1005 $this->writeFile($f,$str);
1009 function writeFile($f, $str)
1011 require_once 'System.php';
1012 System::mkdir(array('-p', dirname($f)));
1015 file_put_contents($f, $str);