Guest
Public paste!

ruxkor

By: a guest | Dec 17th, 2008 | Syntax: PHP | Size: 11.03 KB | Hits: 341 | Expires: Never
This paste has a previous version, view the difference. Copy text to clipboard
  1. <?php
  2. /**
  3.  * Visualize console task
  4.  *
  5.  * This task can be used to generate a graphical representation of your tables or models.
  6.  *
  7.  * PHP versions 4 and 5
  8.  *
  9.  * Copyright (c) Tomenko Yevgeny
  10.  *
  11.  * Licensed under The MIT License
  12.  * Redistributions of files must retain the above copyright notice.
  13.  *
  14.  * @version             1.2.0
  15.  * @modifiedby          Andy Dawson
  16.  * @lastmodified        2007-12-20 23:39:02 +0100 (Thu, 16 Aug 2007) $
  17.  * @license             http://www.opensource.org/licenses/mit-license.php The MIT License
  18.  */
  19. uses('Folder','File','model'.DS.'connection_manager');
  20. class VisualizeShell extends Shell {
  21.  
  22.         var $DOC_DIR ;
  23.  
  24.         var $PREFIX = APP_DIR;
  25.  
  26.         var $graphToolPath = 'dot.exe'; // cake visualize -tool C:\dev\_tools_\graphviz-2.16\bin\dot.exe
  27.  
  28.  
  29.         function help() {
  30.                 $this->out('CakePHP visualise, Usage examples:');
  31.                 $this->out('cake visualize help');
  32.                 $this->out('    - this text');
  33.                 $this->out('cake visualize tables');
  34.                 $this->out('    - generate graphic based on table structure');
  35.                 $this->out('cake visualize models');
  36.                 $this->out('    - generate graphic based on model association definitions');
  37.                 $this->out('cake visualise [-tool graphVizTool]');
  38.                 $this->hr();
  39.         }
  40.  
  41.         function initialize() {
  42.                 if (DS == '/') {
  43.                         $this->graphToolPath = array(
  44.                                 'dot',
  45.                                 'dot -Gmode=heir',
  46.                                 'neato',
  47.                                 'neato -Gmodel=subset'
  48.                         );
  49.                 }
  50.                 $this->DOC_DIR = APP . 'config' . DS . 'sql';
  51.                 $this->PREFIX= 'img_';
  52.                 if (isset($this->params['tool'])) {
  53.                         $this->graphToolPath = $this->params['tool'];
  54.                 }
  55.                 return true;
  56.         }
  57.  
  58.         function main() {
  59.                 if (!isset($this->args[0])) {
  60.                         $this->args[0] = 'filtered';
  61.                 }
  62.                 if ($this->args[0] == 'help') {
  63.                         $this->help();
  64.                         return;
  65.                 } elseif ($this->args[0] == 'tables') {
  66.                         $mode = 't';
  67.                         $this->generateDataFromTables();    
  68.                 } elseif ($this->args[0] == 'models') {
  69.                         $mode = 'm';
  70.                         $this->generateDataFromModels();    
  71.                 } elseif ($this->args[0] == 'combined') {
  72.                         $mode = 'combined';
  73.                         $this->generateDataFromTables();
  74.                         $this->generateDataFromModels();
  75.                 } elseif ($this->args[0] == 'filtered') {
  76.                         $mode = 'filtered';
  77.                         $this->generateDataFromTables();
  78.                         $this->generateDataFromModels();
  79.                         $this->filterPotentialDuplicates();
  80.                 }
  81.                
  82.                 $this->writeDotFile($this->DOC_DIR, $mode);
  83.         }
  84.  
  85.         function generateDataFromModels() {
  86.                 foreach($this->getAllModels() as $model) {
  87.                         $this->out("Looking at model: {$model}");
  88.                         $model = new $model();
  89.                         if (!$model->useTable) {
  90.                                 continue;
  91.                         }
  92.                         $this->data['tables'][$model->name] = $model->schema(true);
  93.                         foreach ($this->data['tables'][$model->name] as $attrname => $attr) {
  94.                                 if (!empty($attr['length'])) {
  95.                                         $attr['type'] .= "[{$attr['length']}]";
  96.                                 }
  97.                                 $this->data['nodes'][$model->name][$attrname] = $attr['type'];
  98.                                 if (!empty($attr['default'])) {
  99.                                         $this->data['nodes'][$model->name][$attrname] .= ", default: \\\"{$attr['default']}\\\"";
  100.                                 }
  101.                         }
  102.                         foreach($model->__associations as $type) {
  103.                                 foreach ($model->$type as $alias => $association) {
  104.                                         $otherModel = $association['className'];
  105.                                         if ($type == 'belongsTo') {
  106.                                                 $this->data['associations'][$model->name.$otherModel] =
  107.                                                         array(
  108.                                                                 'label'=> $model->name . '->' . $alias,
  109.                                                                 'node1'=> $model->name,
  110.                                                                 'node2'=> $otherModel,
  111.                                                                 'foreignKey' => $association['foreignKey'],
  112.                                                                 'assType'=>'n:1'
  113.                                                         );
  114.                                         } elseif (in_array($type, array('hasOne', 'hasMany'))) {
  115.                                                 $this->data['associations'][$otherModel.$model->name] =
  116.                                                         array(
  117.                                                                 'label'=> $otherModel . '->' . $model->name,
  118.                                                                 'node1'=> $otherModel,
  119.                                                                 'node2'=> $model->name,
  120.                                                                 'foreignKey' => $association['foreignKey'],
  121.                                                                 'assType'=>($type=='hasOne')?'1:1':'n:1'
  122.                                                         );
  123.                                         } elseif ($type == 'hasAndBelongsToMany') {
  124.                                                 $names[] = $model->name;
  125.                                                 $names[] = $otherModel;
  126.                                                 sort($names);
  127.                                                 $modelName = implode($names, '');
  128.                                                 if (!isset($modelName)) {
  129.                                                         $DynamicModel = new Model(array('name'=> $modelName, 'table'=> $association['joinTable']));
  130.                                                         $this->data['tables'][$modelName] = $DynamicModel->schema(true);
  131.                                                         foreach ($this->data['tables'][$modelName] as $attrname => $attr) {
  132.                                                                 if (!empty($attr['length'])) {
  133.                                                                         $attr['type'] .= "[{$attr['length']}]";
  134.                                                                 }
  135.                                                                 $this->data['nodes'][$modelName][$attrname] = $attr['type'];
  136.                                                                 $attrtype = $attr['type'];
  137.                                                                 if (!empty($attr['default'])) {
  138.                                                                         $this->data['nodes'][$modelName][$attrname] .= ", default: \\\"{$attr['default']}\\\"";
  139.                                                                 }
  140.                                                         }
  141.                                                         $this->data['associations'][$model->name.$otherModel] =
  142.                                                                 array(
  143.                                                                         'label'=> $model->name . '->' . $modelName,
  144.                                                                         'node1'=> $model->name,
  145.                                                                         'node2'=> $modelName,
  146.                                                                         'foreignKey' => $association['foreignKey'],
  147.                                                                         'assType'=>'1:n'
  148.                                                                 );
  149.                                                         $this->data['associations'][$otherModel.$model->name] =
  150.                                                                 array(
  151.                                                                         'label'=> $otherModel . '->' . $modelName,
  152.                                                                         'node1'=> $otherModel,
  153.                                                                         'node2'=> $modelName,
  154.                                                                         'foreignKey' => $association['foreignKey'],
  155.                                                                         'assType'=>'1:n'
  156.                                                                 );
  157.                                                 }
  158.                                         }
  159.                                 }
  160.                         }
  161.                 }
  162.         }
  163.  
  164.         function generateDataFromTables() {
  165.                 foreach($this->getAllTables() as $table_name) {
  166.                         $this->out("Looking at table: {$table_name}");
  167.                         $modelName=$this->_modelName($table_name);
  168.                         $this->data['tables'][$modelName] = $this->getSchemaInfo($modelName,$table_name);
  169.                 }
  170.                 foreach ($this->data['tables'] as $table => $attributes) {
  171.                         if (is_array($attributes) && count($attributes)>0) {
  172.                                 foreach ($attributes as $attrname => $attr) {
  173.                                         if (substr($attrname, -3) == '_id') {
  174.                                                 # Create an association to other table
  175.                                                 $otherTable = Inflector::camelize(r('_id','',$attrname));
  176.                                                 if (!empty($this->data['tables'][$otherTable])) {
  177.                                                         $other_table = $this->data['tables'][$otherTable];
  178.                                                         $this->data['associations'][] = array('label'=> $attrname, 'node1'=> $table, 'node2'=> $otherTable);
  179.                                                 }
  180.                                         }
  181.                                         if (!empty($attr['length'])) {
  182.                                                 $attr['type'] .= "[{$attr['length']}]";
  183.                                         }
  184.                                         $this->data['nodes'][$table][$attrname] = $attr['type'];
  185.                                         $attrtype = $attr['type'];
  186.                                         if (!empty($attr['default'])) {
  187.                                                 $this->data['nodes'][$table][$attrname] .= ", default: \\\"{$attr['default']}\\\"";
  188.                                         }
  189.                                 }
  190.                         }
  191.                 }
  192.         }
  193.  
  194.         function getAllModels() {
  195.                 $Inflector =& Inflector::getInstance();
  196.                 uses('Folder');
  197.                 $folder = new Folder(MODELS);
  198.                 $models = $folder->findRecursive('.*php');
  199.                 $folder = new Folder(BEHAVIORS);
  200.                 $behaviors = $folder->findRecursive('.*php');
  201.                 $models = array_diff($models, $behaviors);
  202.                 foreach ($models as $id => $model) {
  203.                         $file = new File($model);
  204.                         $models[$id] = $file->name();
  205.                 }
  206.                 $models = array_map(array(&$Inflector, 'camelize'), $models);
  207.                 App::import('Model', $models);
  208.                 return $models;
  209.         }
  210.  
  211.         function getAllTables($useDbConfig = 'default') {
  212.                 $db =& ConnectionManager::getDataSource($useDbConfig);
  213.                 $usePrefix = empty($db->config['prefix']) ? '': $db->config['prefix'];
  214.                 if ($usePrefix) {
  215.                         $tables = array();
  216.                         foreach ($db->listSources() as $table) {
  217.                                 if (!strncmp($table, $usePrefix, strlen($usePrefix))) {
  218.                                         $tables[] = substr($table, strlen($usePrefix));
  219.                                 }
  220.                         }
  221.                 } else {
  222.                         $tables = $db->listSources();
  223.                 }
  224.                 $this->__tables = $tables;
  225.                 return $tables;
  226.         }
  227.  
  228.         function getSchemaInfo($modelName,$table_name) {
  229.                 $attrs = array();
  230.                 if (App::import('model',$modelName)) {
  231.                         $model = & new $modelName();
  232.                         $attrs=$model->schema();
  233.                         return $attrs;
  234.                 } else {
  235.                         $DynamicModel = new Model(array('name'=> $modelName, 'table'=> $table_name));
  236.                         $attrs=$DynamicModel->schema();
  237.                         return $attrs;
  238.                 }
  239.                 return false;
  240.         }  
  241.  
  242.         function writeDotFile($target_dir, $mode) {
  243.                 if (!file_exists($target_dir) || !is_dir($target_dir)) {
  244.                         $this->out("Creating directory \"{$target_dir}\"&#65533;");
  245.                         $folder = & new Folder($target_dir, true);
  246.                 }
  247.                 $header = $this->PREFIX+strftime('%Y-%m-%d %H:%M:%S',time());
  248.                 $version=0;
  249.                 if ($version > 0) {
  250.                         $header .= "\\nSchema version $version";
  251.                 }
  252.                 $dotFile = $target_dir .DS. 'mode_' . $mode . '.dot';
  253.                 if (file_exists($dotFile)) {
  254.                         $f = & new File($dotFile);
  255.                         $f->delete();
  256.                 }
  257.                 $f = & new File($dotFile, true );
  258.  
  259.                 // Define a graph and some global settings
  260.                 $f->append("digraph G {\n");
  261.                 $f->append("\toverlap=false;\n");
  262.                 $f->append("\tsplines=true;\n");
  263.                 $f->append("\tnode [fontname=\"Helvetica\",fontsize=9];\n");
  264.                 $f->append("\tedge [fontname=\"Helvetica\",fontsize=8];\n");
  265.                 $f->append("\tranksep=0.1;\n");
  266.                 $f->append("\tnodesep=0.1;\n");
  267. //              $f->append("\tedge [decorate=\"true\"];\n");
  268.                 // Write header info
  269.                 $f->append("\t_schema_info [shape=\"plaintext\", label=\"{$header}\", fontname=\"Helvetica\",fontsize=8];\n");
  270.  
  271.                 $assocs = array();
  272.                 // Draw the tables as boxes
  273.                
  274.                 foreach ($this->data['nodes'] as $table=>$attributes) {
  275.                         $f->append("\t\"{$table}\" [label=\"{{$table}|");
  276.                         foreach ($attributes as $field=>$label) {
  277.                                 $f->append("{$field} : {$label}\\n");
  278.                         }
  279.                         $f->append("}\" shape=\"record\"];\n");
  280.                 }
  281.                 // Draw the relations
  282.                 foreach ($this->data['associations'] as $assoc) {
  283.                         if (isset($assoc['assType'])) $assoc['label'].= ' ('.$assoc['assType'].')';
  284.                         $f->append("\t\"{$assoc['node1']}\" -> \"{$assoc['node2']}\" [label=\"{$assoc['label']}\"]\n");
  285.                 }
  286.  
  287.                 // Close the graph
  288.                 $f->append("}\n");
  289.                 $f->close();        // Create the images by using dot and neato (grapviz tools)
  290.                 $this->out("Generated {$dotFile}\n");
  291.  
  292.                 $this->createImgs($dotFile, $target_dir, $mode);
  293.  
  294.                 // Remove the .dot file // Keep it for debugging and general info
  295.                 $f->delete();
  296.         }
  297.  
  298.         function createImgs($dotFile, $path, $mode) {
  299.                 if (is_string($this->graphToolPath)) {
  300.                         $commands = array($this->graphToolPath);
  301.                 } else {
  302.                         $commands = $this->graphToolPath;
  303.                 }
  304.                 uses ('Sanitize');
  305.                 foreach ($commands as $command) {
  306.                         $imgFile = $path . DS . 'schematic_' . $mode . '_' . Sanitize::paranoid($command) . ".png";
  307.                         if (file_exists($imgFile)) {
  308.                                 $f = & new File($imgFile);
  309.                                 $f->delete();
  310.                         }
  311.                         if ($this->createImg($command, $dotFile, $imgFile)) {
  312.                                 $this->out("Generated {$imgFile}\n");
  313.                         } else {
  314.                                 break;
  315.                         }
  316.                 }
  317.         }
  318.  
  319.         function createImg($command, $dotFile, $imgFile) {
  320.                 $command = $command.' -v -Tpng -o"'.$imgFile.'" "'.$dotFile.'"';
  321.                 ob_start();
  322.                 system($command,$return);
  323.                 ob_clean();
  324.                 if ($return != 0) {
  325.                         $this->out("Command Error ($return):\n");          
  326.                         $this->out("$command\n");
  327.                         return false;
  328.                 }
  329.                 return true;
  330.         }
  331.         function filterPotentialDuplicates() {
  332.                 $associations =& $this->data['associations'];
  333.                 $duplicates = array();
  334.                 foreach ($associations as $k=>$v) {
  335.                         if (is_numeric($k)) {
  336.                                 foreach ($associations as $ik=>$iv) {
  337.                                         if (
  338.                                                 (!is_numeric($ik)) &&
  339.                                                 ($iv['node1']==$v['node1']) &&
  340.                                                 ($iv['node2']==$v['node2']) &&
  341.                                                 ($iv['foreignKey']==$v['label'])
  342.                                         ) {
  343.                                                  $duplicates[] = $k;
  344.                                         }
  345.                                 }
  346.                         }
  347.                 }
  348.                 foreach ($duplicates as $k) {
  349.                         unset($associations["$k"]);
  350.                 }
  351.         }
  352. }
  353. ?>