1<?php 2/** 3 * Contains PhpMyAdmin\Plugins\Schema\Svg\RelationStatsSvg class 4 */ 5 6declare(strict_types=1); 7 8namespace PhpMyAdmin\Plugins\Schema\Svg; 9 10use PhpMyAdmin\Plugins\Schema\Dia\TableStatsDia; 11use PhpMyAdmin\Plugins\Schema\Eps\TableStatsEps; 12use PhpMyAdmin\Plugins\Schema\ExportRelationSchema; 13use PhpMyAdmin\Plugins\Schema\Pdf\TableStatsPdf; 14use function in_array; 15use function max; 16use function min; 17use function sprintf; 18 19/** 20 * RelationStatsSvg Relation Schema Class 21 * 22 * Purpose of this class is to generate the SVG XML Document because 23 * SVG defines the graphics in XML format which is used for representing 24 * the database diagrams as vector image. This class actually helps 25 * in preparing SVG XML format. 26 * 27 * SVG XML is generated by using XMLWriter php extension and this class 28 * inherits ExportRelationSchema class has common functionality added 29 * to this class 30 * 31 * @name Svg_Relation_Schema 32 */ 33class SvgRelationSchema extends ExportRelationSchema 34{ 35 /** @var TableStatsDia[]|TableStatsEps[]|TableStatsPdf[]|TableStatsSvg[] */ 36 private $tables = []; 37 38 /** @var RelationStatsSvg[] Relations */ 39 private $relations = []; 40 41 /** @var int|float */ 42 private $xMax = 0; 43 44 /** @var int|float */ 45 private $yMax = 0; 46 47 /** @var int|float */ 48 private $xMin = 100000; 49 50 /** @var int|float */ 51 private $yMin = 100000; 52 53 /** @var int */ 54 private $tablewidth; 55 56 /** 57 * Upon instantiation This starts writing the SVG XML document 58 * user will be prompted for download as .svg extension 59 * 60 * @see PMA_SVG 61 * 62 * @param string $db database name 63 */ 64 public function __construct($db) 65 { 66 parent::__construct($db, new Svg()); 67 68 $this->setShowColor(isset($_REQUEST['svg_show_color'])); 69 $this->setShowKeys(isset($_REQUEST['svg_show_keys'])); 70 $this->setTableDimension(isset($_REQUEST['svg_show_table_dimension'])); 71 $this->setAllTablesSameWidth(isset($_REQUEST['svg_all_tables_same_width'])); 72 73 $this->diagram->setTitle( 74 sprintf( 75 __('Schema of the %s database - Page %s'), 76 $this->db, 77 $this->pageNumber 78 ) 79 ); 80 $this->diagram->SetAuthor('phpMyAdmin ' . PMA_VERSION); 81 $this->diagram->setFont('Arial'); 82 $this->diagram->setFontSize(16); 83 84 $alltables = $this->getTablesFromRequest(); 85 86 foreach ($alltables as $table) { 87 if (! isset($this->tables[$table])) { 88 $this->tables[$table] = new TableStatsSvg( 89 $this->diagram, 90 $this->db, 91 $table, 92 $this->diagram->getFont(), 93 $this->diagram->getFontSize(), 94 $this->pageNumber, 95 $this->tablewidth, 96 $this->showKeys, 97 $this->tableDimension, 98 $this->offline 99 ); 100 } 101 102 if ($this->sameWide) { 103 $this->tables[$table]->width = &$this->tablewidth; 104 } 105 $this->setMinMax($this->tables[$table]); 106 } 107 108 $border = 15; 109 $this->diagram->startSvgDoc( 110 $this->xMax + $border, 111 $this->yMax + $border, 112 $this->xMin - $border, 113 $this->yMin - $border 114 ); 115 116 $seen_a_relation = false; 117 foreach ($alltables as $one_table) { 118 $exist_rel = $this->relation->getForeigners($this->db, $one_table, '', 'both'); 119 if (! $exist_rel) { 120 continue; 121 } 122 123 $seen_a_relation = true; 124 foreach ($exist_rel as $master_field => $rel) { 125 /* put the foreign table on the schema only if selected 126 * by the user 127 * (do not use array_search() because we would have to 128 * to do a === false and this is not PHP3 compatible) 129 */ 130 if ($master_field !== 'foreign_keys_data') { 131 if (in_array($rel['foreign_table'], $alltables)) { 132 $this->addRelation( 133 $one_table, 134 $this->diagram->getFont(), 135 $this->diagram->getFontSize(), 136 $master_field, 137 $rel['foreign_table'], 138 $rel['foreign_field'], 139 $this->tableDimension 140 ); 141 } 142 continue; 143 } 144 145 foreach ($rel as $one_key) { 146 if (! in_array($one_key['ref_table_name'], $alltables)) { 147 continue; 148 } 149 150 foreach ($one_key['index_list'] as $index => $one_field) { 151 $this->addRelation( 152 $one_table, 153 $this->diagram->getFont(), 154 $this->diagram->getFontSize(), 155 $one_field, 156 $one_key['ref_table_name'], 157 $one_key['ref_index_list'][$index], 158 $this->tableDimension 159 ); 160 } 161 } 162 } 163 } 164 if ($seen_a_relation) { 165 $this->drawRelations(); 166 } 167 168 $this->drawTables(); 169 $this->diagram->endSvgDoc(); 170 } 171 172 /** 173 * Output RelationStatsSvg Document for download 174 * 175 * @return void 176 */ 177 public function showOutput() 178 { 179 $this->diagram->showOutput($this->getFileName('.svg')); 180 } 181 182 /** 183 * Sets X and Y minimum and maximum for a table cell 184 * 185 * @param TableStatsSvg $table The table 186 * 187 * @return void 188 */ 189 private function setMinMax($table) 190 { 191 $this->xMax = max($this->xMax, $table->x + $table->width); 192 $this->yMax = max($this->yMax, $table->y + $table->height); 193 $this->xMin = min($this->xMin, $table->x); 194 $this->yMin = min($this->yMin, $table->y); 195 } 196 197 /** 198 * Defines relation objects 199 * 200 * @see setMinMax,TableStatsSvg::__construct(), 201 * PhpMyAdmin\Plugins\Schema\Svg\RelationStatsSvg::__construct() 202 * 203 * @param string $masterTable The master table name 204 * @param string $font The font face 205 * @param int $fontSize Font size 206 * @param string $masterField The relation field in the master table 207 * @param string $foreignTable The foreign table name 208 * @param string $foreignField The relation field in the foreign table 209 * @param bool $tableDimension Whether to display table position or not 210 * 211 * @return void 212 */ 213 private function addRelation( 214 $masterTable, 215 $font, 216 $fontSize, 217 $masterField, 218 $foreignTable, 219 $foreignField, 220 $tableDimension 221 ) { 222 if (! isset($this->tables[$masterTable])) { 223 $this->tables[$masterTable] = new TableStatsSvg( 224 $this->diagram, 225 $this->db, 226 $masterTable, 227 $font, 228 $fontSize, 229 $this->pageNumber, 230 $this->tablewidth, 231 false, 232 $tableDimension 233 ); 234 $this->setMinMax($this->tables[$masterTable]); 235 } 236 if (! isset($this->tables[$foreignTable])) { 237 $this->tables[$foreignTable] = new TableStatsSvg( 238 $this->diagram, 239 $this->db, 240 $foreignTable, 241 $font, 242 $fontSize, 243 $this->pageNumber, 244 $this->tablewidth, 245 false, 246 $tableDimension 247 ); 248 $this->setMinMax($this->tables[$foreignTable]); 249 } 250 $this->relations[] = new RelationStatsSvg( 251 $this->diagram, 252 $this->tables[$masterTable], 253 $masterField, 254 $this->tables[$foreignTable], 255 $foreignField 256 ); 257 } 258 259 /** 260 * Draws relation arrows and lines 261 * connects master table's master field to 262 * foreign table's foreign field 263 * 264 * @see Relation_Stats_Svg::relationDraw() 265 * 266 * @return void 267 */ 268 private function drawRelations() 269 { 270 foreach ($this->relations as $relation) { 271 $relation->relationDraw($this->showColor); 272 } 273 } 274 275 /** 276 * Draws tables 277 * 278 * @see TableStatsSvg::Table_Stats_tableDraw() 279 * 280 * @return void 281 */ 282 private function drawTables() 283 { 284 foreach ($this->tables as $table) { 285 $table->tableDraw($this->showColor); 286 } 287 } 288} 289