├── LICENSE.md ├── bin ├── mysql-draw └── stub.php ├── build.svg ├── build.xml ├── compose.yaml ├── composer.json └── src └── Cli.php /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright © `2021` `Jawira Portugal` 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy of this software and associated documentation 8 | files (the “Software”), to deal in the Software without 9 | restriction, including without limitation the rights to use, 10 | copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /bin/mysql-draw: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | __DIR__ . DIRECTORY_SEPARATOR . '../vendor/autoload.php', 9 | 'as library' => __DIR__ . DIRECTORY_SEPARATOR . '../../../vendor/autoload.php',]; 10 | array_walk($autoloads, function (string $autoload) { 11 | if (is_file($autoload)) { 12 | require $autoload; 13 | } 14 | }); 15 | } 16 | 17 | function main(): void 18 | { 19 | try { 20 | $code = 0; 21 | autoload(); 22 | Cli::main(); 23 | } catch (Throwable $throwable) { 24 | echo $throwable->getMessage() . PHP_EOL; 25 | $code = 1; 26 | } finally { 27 | exit($code); 28 | } 29 | } 30 | 31 | main(); 32 | -------------------------------------------------------------------------------- /bin/stub.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getMessage() . PHP_EOL; 13 | exit(1); 14 | } 15 | exit(0); 16 | __HALT_COMPILER(); 17 | -------------------------------------------------------------------------------- /build.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 9 | MySQL Draw 11 | 12 | 13 | 15 | setup 17 | 18 | 19 | 20 | 22 | composer:install-dev 24 | 25 | 26 | 27 | 29 | dc:up 31 | 32 | 33 | 34 | 36 | dc:ip 38 | 39 | 40 | 41 | 43 | qa 45 | 46 | 47 | 48 | 50 | composer:validate 52 | 53 | 54 | 55 | 57 | composer:normalize 59 | 60 | 61 | 62 | 64 | php:lint 66 | 67 | 68 | 69 | 71 | phpstan:analyse 73 | 74 | 75 | 76 | 78 | phar 80 | 81 | 82 | 83 | 85 | composer:install-prod 87 | 88 | 89 | 90 | 92 | phar:build 94 | 95 | 96 | 97 | 99 | help 101 | 102 | 103 | 104 | 106 | visualizer 108 | 109 | 110 | 111 | 113 | dc:stop 115 | 116 | 117 | 118 | 120 | dc:down 122 | 123 | 125 | 126 | 128 | 131 | depend:1 133 | 134 | 136 | 137 | 139 | 141 | depend:2 143 | 144 | 146 | 147 | 149 | 152 | depend:3 154 | 155 | 157 | 158 | 160 | 163 | depend:1 165 | 166 | 168 | 169 | 171 | 174 | depend:2 176 | 177 | 179 | 180 | 182 | 185 | depend:3 187 | 188 | 190 | 191 | 193 | 196 | depend:4 198 | 199 | 201 | 202 | 204 | 206 | depend:1 208 | 209 | 211 | 212 | 214 | 217 | depend:2 219 | 220 | 222 | 223 | 225 | 228 | depend:3 230 | 231 | 233 | 234 | 236 | 238 | call:1 240 | 241 | 295 | 296 | 297 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | 4 | employees: 5 | image: genschsa/mysql-employees 6 | environment: 7 | MYSQL_DATABASE: employees 8 | MYSQL_USER: groot 9 | MYSQL_PASSWORD: groot 10 | MYSQL_ROOT_PASSWORD: groot 11 | networks: 12 | - default 13 | 14 | sakila: 15 | image: sakiladb/mysql 16 | environment: 17 | MYSQL_DATABASE: sakila 18 | MYSQL_USER: groot 19 | MYSQL_PASSWORD: groot 20 | MYSQL_ROOT_PASSWORD: groot 21 | networks: 22 | - default 23 | 24 | puml: 25 | image: plantuml/plantuml-server:tomcat 26 | labels: 27 | - 'traefik.enable=true' 28 | environment: 29 | PLANTUML_LIMIT_SIZE: 10000 30 | networks: 31 | - default 32 | - traefik_default 33 | 34 | networks: 35 | default: 36 | traefik_default: 37 | external: true 38 | 39 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jawira/mysql-draw", 3 | "description": "\ud83d\udcd0 Minimalist standalone tool to generate MySQL database diagrams.", 4 | "license": "MIT", 5 | "type": "library", 6 | "keywords": [ 7 | "database", 8 | "diagram", 9 | "doctrine", 10 | "uml", 11 | "symfony" 12 | ], 13 | "authors": [ 14 | { 15 | "name": "Jawira Portugal", 16 | "email": "dev@tugal.be" 17 | } 18 | ], 19 | "support": { 20 | "issues": "https://github.com/jawira/mysql-draw/issues" 21 | }, 22 | "require": { 23 | "php": "^7.4 || ^8.0", 24 | "doctrine/dbal": "^2.5", 25 | "jawira/db-draw": "^1.2", 26 | "jawira/mini-getopt": "^1.2", 27 | "jawira/plantuml-client": "^1.0", 28 | "jawira/plantuml-to-image": "^0.2.0" 29 | }, 30 | "require-dev": { 31 | "ergebnis/composer-normalize": "^2.15", 32 | "jawira/skeleton": "^2.6", 33 | "phpstan/phpstan": "^0.12.99" 34 | }, 35 | "suggest": { 36 | "phing/phing": "PHP Build Tool" 37 | }, 38 | "autoload": { 39 | "psr-4": { 40 | "Jawira\\MysqlDraw\\": "src/" 41 | } 42 | }, 43 | "config": { 44 | "allow-plugins": { 45 | "ergebnis/composer-normalize": true 46 | }, 47 | "platform-check": true, 48 | "preferred-install": "dist", 49 | "sort-packages": true 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Cli.php: -------------------------------------------------------------------------------- 1 | addRequired('u', 'url', 'Database url in doctrine format.', 'url'); 30 | $mg->addRequired('f', 'format', 'Diagram format (default: svg).', 'puml|svg|png'); 31 | $mg->addRequired('s', 'size', 'Diagram size (default: midi).', 'mini|midi|maxi'); 32 | $mg->addRequired('j', 'jar', "Path to plantuml.jar.", '/path/to/plantuml.jar'); 33 | $mg->addNoValue('h', 'help', 'Show help'); 34 | $getopt = $mg->getopt(); 35 | $usage = [ 36 | 'mysql-draw --url=mysql://user:pass@host/db_name --size=mini --format=png', 37 | 'mysql-draw --url=mysql://user:pass@host/db_name --jar=/usr/share/plantuml/plantuml.jar', 38 | 'mysql-draw --url=mysql://user:pass@host:3306/db_name?serverVersion=5.7', 39 | 'mysql-draw --url=mysql://user:pass@host/db_name?serverVersion=mariadb-10.3.22', 40 | 'DATABASE_URL=mysql://user:pass@host/db_name mysql-draw', 41 | ]; 42 | $doc = $mg->doc('Generate a diagram from your MySQL database.', $usage); 43 | 44 | return compact('getopt', 'doc'); 45 | } 46 | 47 | /** 48 | * @throws \Doctrine\DBAL\Exception 49 | * @throws \Jawira\MiniGetopt\MiniGetoptException 50 | * @throws \Exception 51 | */ 52 | static public function main(): void 53 | { 54 | ['getopt' => $getopt, 'doc' => $doc] = self::loadOptions(); 55 | 56 | $env = getenv('DATABASE_URL') ?: null; 57 | $url = $getopt['u'] ?? $getopt['url'] ?? $env; 58 | $format = $getopt['f'] ?? $getopt['format'] ?? 'svg'; 59 | $size = $getopt['s'] ?? $getopt['size'] ?? 'midi'; 60 | $jar = $getopt['j'] ?? $getopt['jar'] ?? null; 61 | $help = $getopt['h'] ?? $getopt['help'] ?? null; 62 | 63 | if (empty($getopt) && empty($env)) { 64 | echo $doc; 65 | exit(1); 66 | } 67 | 68 | if ($help === false) { 69 | echo $doc; 70 | exit(0); 71 | } 72 | 73 | if (empty($url)) { 74 | throw new RuntimeException('Database url not set, use --url or DATABASE_URL environment variable.'); 75 | } 76 | 77 | $puml = self::generatePuml($url, $size); 78 | $diagram = self::generateDiagram($puml, $format, $jar); 79 | $filename = "database.$format"; 80 | echo "Writing $filename...", PHP_EOL; 81 | file_put_contents($filename, $diagram); 82 | echo 'mysql-draw by Jawira Portugal', PHP_EOL; 83 | } 84 | 85 | /** 86 | * @return string 87 | * @throws \Doctrine\DBAL\Exception 88 | */ 89 | protected static function generatePuml(string $url, string $size, ?string $jar = null): string 90 | { 91 | $params = ['url' => $url, 92 | 'driver' => 'pdo_mysql']; 93 | $connection = DriverManager::getConnection($params); 94 | $connection->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string'); 95 | $connection->getDatabasePlatform()->registerDoctrineTypeMapping('geometry', 'string'); 96 | $dbDraw = new DbDraw($connection); 97 | 98 | return $dbDraw->generatePuml($size); 99 | } 100 | 101 | /** 102 | * Convert PlantUML diagram to image. 103 | * 104 | * @param string $puml PlantUML diagram. 105 | * @param string $format Target format. 106 | * 107 | * @return string 108 | * @throws \Jawira\PlantUmlClient\ClientException 109 | * @throws PlantUmlException 110 | */ 111 | protected static function generateDiagram(string $puml, string $format, ?string $jar = null): string 112 | { 113 | if ($format === 'puml') { 114 | return $puml; 115 | } 116 | if (!in_array($format, [Format::PNG, Format::SVG])) { 117 | throw new RuntimeException("Invalid format $format"); 118 | } 119 | 120 | // Trying to use PlantUml locally 121 | $plantUmlToImage = new PlantUml(); 122 | if (is_string($jar)) { 123 | echo "Setting Jar $jar...", PHP_EOL; 124 | $plantUmlToImage->setJar($jar); 125 | } 126 | echo 'Trying to use PlantUML locally...', PHP_EOL; 127 | if ($plantUmlToImage->isPlantUmlAvailable()) { 128 | return $plantUmlToImage->convertTo($puml, $format); 129 | } else { 130 | echo 'PlantUML not found locally...', PHP_EOL; 131 | } 132 | 133 | // Using web client 134 | $client = new Client(); 135 | $server = $client->getServer(); 136 | echo "Trying to use PlantUML web server $server...", PHP_EOL; 137 | return $client->generateImage($puml, $format); 138 | } 139 | 140 | } 141 | --------------------------------------------------------------------------------