├── 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 |
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 |
--------------------------------------------------------------------------------