Skip to content

Commit 899b0d5

Browse files
authored
Merge pull request #122 from beyondcode/dev
5.0.0
2 parents 07254be + 0277242 commit 899b0d5

File tree

6 files changed

+314
-15
lines changed

6 files changed

+314
-15
lines changed

.github/workflows/tests.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: run-tests
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
- dev
8+
pull_request:
9+
branches:
10+
- master
11+
12+
jobs:
13+
php-tests:
14+
runs-on: ubuntu-latest
15+
16+
strategy:
17+
fail-fast: true
18+
matrix:
19+
php: ['8.4', '8.3', '8.2']
20+
laravel: ['10.*', '11.*', '12.*']
21+
dependency-version: [prefer-stable]
22+
exclude:
23+
- php: 8.4
24+
laravel: 10.*
25+
- php: 8.4
26+
laravel: 11.*
27+
include:
28+
- laravel: 10.*
29+
testbench: 8.*
30+
- laravel: 11.*
31+
testbench: 9.*
32+
- laravel: 12.*
33+
testbench: 10.*
34+
35+
name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} - ubuntu-latest
36+
37+
steps:
38+
- name: Checkout code
39+
uses: actions/checkout@v4
40+
41+
- name: Install Graphviz
42+
run: sudo apt-get update && sudo apt-get install -y graphviz
43+
44+
- name: Setup PHP
45+
uses: shivammathur/setup-php@v2
46+
with:
47+
php-version: ${{ matrix.php }}
48+
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick
49+
coverage: none
50+
51+
- name: Install dependencies
52+
run: |
53+
composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update
54+
if [[ "${{ matrix.laravel }}" == "10.*" ]]; then composer require "doctrine/dbal:^3.3" --no-interaction --no-update; fi
55+
composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction
56+
57+
- name: Execute tests
58+
run: vendor/bin/phpunit

README.md

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,52 @@ Or use one of the other [output formats](https://www.graphviz.org/doc/info/outpu
8484
php artisan generate:erd output.svg --format=svg
8585
```
8686

87+
### Text Output
88+
89+
If you want to generate a text representation of the ER diagram instead of an image, you can use the `--text-output` option:
90+
91+
```bash
92+
php artisan generate:erd output.txt --text-output
93+
```
94+
95+
This will generate a text file with the GraphViz DOT representation of the ER diagram.
96+
97+
### Structured Text Output for AI Models
98+
99+
If you want to generate a structured text representation of the ER diagram that is more suitable for AI models, simply specify a filename with a `.txt` extension:
100+
101+
```bash
102+
php artisan generate:erd output.txt
103+
```
104+
105+
This will automatically generate a Markdown file with a structured representation of the entities and their relationships, which can be used as context for AI models.
106+
107+
#### Output Format
108+
109+
The structured output format looks like this:
110+
111+
```markdown
112+
# Entity Relationship Diagram
113+
114+
## Entities
115+
116+
### User (`App\Models\User`)
117+
118+
#### Attributes:
119+
- `id` (integer)
120+
- `name` (string)
121+
- `email` (string)
122+
...
123+
124+
## Relationships
125+
126+
### User Relationships
127+
- **HasMany** `posts` to Post (Local Key: `id`, Foreign Key: `user_id`)
128+
...
129+
```
130+
131+
This format is particularly useful when providing context to AI models about your database structure.
132+
87133
## Customization
88134

89135
Please take a look at the published `erd-generator.php` configuration file for all available customization options.
@@ -121,4 +167,4 @@ If you discover any security related issues, please email marcel@beyondco.de ins
121167

122168
## License
123169

124-
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
170+
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

composer.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@
1616
}
1717
],
1818
"require": {
19-
"php": "^7.1|^8.0",
20-
"doctrine/dbal": "~2.3|^3.3|^4.0",
19+
"php": "^8.2",
20+
"doctrine/dbal": "^3.3|^4.0",
2121
"phpdocumentor/graphviz": "^1.0",
22-
"nikic/php-parser": "^2.0|^3.0|^4.0|^5.0"
22+
"nikic/php-parser": "^4.0|^5.0"
2323
},
2424
"require-dev": {
2525
"larapack/dd": "^1.0",
26-
"orchestra/testbench": "~3.5|~3.6|~3.7|~3.8|^4.0|^7.0|^8.0|^9.0",
27-
"phpunit/phpunit": "^7.0| ^8.0|^9.5.10|^10.5|^11.0.1",
28-
"spatie/phpunit-snapshot-assertions": "^1.3|^4.2|^5.1"
26+
"orchestra/testbench": "^8.0|^9.0|^10.0",
27+
"phpunit/phpunit": "^9.5.10|^10.5|^11.0",
28+
"spatie/phpunit-snapshot-assertions": "^4.2|^5.1"
2929
},
3030
"autoload": {
3131
"psr-4": {

src/GenerateDiagramCommand.php

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class GenerateDiagramCommand extends Command
1919
*
2020
* @var string
2121
*/
22-
protected $signature = 'generate:erd {filename?} {--format=png}';
22+
protected $signature = 'generate:erd {filename?} {--format=png} {--text-output : Output as text file instead of image}';
2323

2424
/**
2525
* The console command description.
@@ -72,15 +72,39 @@ public function handle()
7272

7373
$graph = $this->graphBuilder->buildGraph($models);
7474

75-
if ($this->option('format') === self::FORMAT_TEXT) {
76-
$this->info($graph->__toString());
75+
// First check for text-output option
76+
if ($this->option('text-output') || $this->option('format') === self::FORMAT_TEXT) {
77+
$textOutput = $graph->__toString();
78+
79+
// If text-output option is set, write to file
80+
if ($this->option('text-output')) {
81+
$outputFileName = $this->getTextOutputFileName();
82+
file_put_contents($outputFileName, $textOutput);
83+
$this->info(PHP_EOL);
84+
$this->info('Wrote text diagram to ' . $outputFileName);
85+
return;
86+
}
87+
88+
// Otherwise just output to console
89+
$this->info($textOutput);
7790
return;
7891
}
7992

80-
$graph->export($this->option('format'), $this->getOutputFileName());
93+
// Then check for .txt extension in filename
94+
$outputFileName = $this->getOutputFileName();
95+
if (pathinfo($outputFileName, PATHINFO_EXTENSION) === 'txt') {
96+
// Generate structured text output for .txt files
97+
$textOutput = $this->graphBuilder->generateStructuredTextRepresentation($models);
98+
file_put_contents($outputFileName, $textOutput);
99+
$this->info(PHP_EOL);
100+
$this->info('Wrote structured ER diagram to ' . $outputFileName);
101+
return;
102+
}
103+
104+
$graph->export($this->option('format'), $outputFileName);
81105

82106
$this->info(PHP_EOL);
83-
$this->info('Wrote diagram to ' . $this->getOutputFileName());
107+
$this->info('Wrote diagram to ' . $outputFileName);
84108
}
85109

86110
protected function getOutputFileName(): string
@@ -89,6 +113,11 @@ protected function getOutputFileName(): string
89113
static::DEFAULT_FILENAME . '.' . $this->option('format');
90114
}
91115

116+
protected function getTextOutputFileName(): string
117+
{
118+
return $this->argument('filename') ?: static::DEFAULT_FILENAME . '.txt';
119+
}
120+
92121
protected function getModelsThatShouldBeInspected(): Collection
93122
{
94123
$directories = config('erd-generator.directories');

src/GraphBuilder.php

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace BeyondCode\ErdGenerator;
44

55
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
6+
use Illuminate\Support\Facades\Schema;
67
use phpDocumentor\GraphViz\Graph;
78
use Illuminate\Support\Collection;
89
use phpDocumentor\GraphViz\Node;
@@ -30,6 +31,75 @@ public function buildGraph(Collection $models) : Graph
3031
return $this->graph;
3132
}
3233

34+
/**
35+
* Generate a structured text representation of the ER diagram
36+
*
37+
* @param Collection $models
38+
* @return string
39+
*/
40+
public function generateStructuredTextRepresentation(Collection $models) : string
41+
{
42+
$output = "# Entity Relationship Diagram\n\n";
43+
44+
// First list all models/entities with their attributes
45+
$output .= "## Entities\n\n";
46+
47+
foreach ($models as $model) {
48+
/** @var Model $model */
49+
$eloquentModel = app($model->getModel());
50+
$output .= "### " . $model->getLabel() . " (`" . $model->getModel() . "`)\n\n";
51+
52+
// Add table columns if available
53+
if (config('erd-generator.use_db_schema')) {
54+
$columns = $this->getTableColumnsFromModel($eloquentModel);
55+
if (count($columns) > 0) {
56+
$output .= "#### Attributes:\n\n";
57+
foreach ($columns as $column) {
58+
if (is_object($column)) {
59+
$name = $column->getName();
60+
$typeName = $column->getType()->getName();
61+
} else {
62+
$name = $column['name'] ?? '';
63+
$typeName = $column['type_name'] ?? '';
64+
}
65+
$columnType = config('erd-generator.use_column_types') ? ' (' . $typeName . ')' : '';
66+
$output .= "- `" . $name . "`" . $columnType . "\n";
67+
}
68+
$output .= "\n";
69+
}
70+
}
71+
}
72+
73+
// Then list all relationships
74+
$output .= "## Relationships\n\n";
75+
76+
foreach ($models as $model) {
77+
/** @var Model $model */
78+
if (count($model->getRelations()) > 0) {
79+
$output .= "### " . $model->getLabel() . " Relationships\n\n";
80+
81+
foreach ($model->getRelations() as $relation) {
82+
/** @var ModelRelation $relation */
83+
// Find the related model by comparing model class names
84+
$relatedModelClass = $relation->getModel();
85+
86+
$relatedModel = $models->first(function ($m) use ($relatedModelClass) {
87+
return $m->getModel() === $relatedModelClass;
88+
});
89+
if ($relatedModel) {
90+
$output .= "- **" . $relation->getType() . "** `" . $relation->getName() . "` to " .
91+
$relatedModel->getLabel() . " (Local Key: `" . $relation->getLocalKey() .
92+
"`, Foreign Key: `" . $relation->getForeignKey() . "`)\n";
93+
}
94+
}
95+
96+
$output .= "\n";
97+
}
98+
}
99+
100+
return $output;
101+
}
102+
33103
protected function getTableColumnsFromModel(EloquentModel $model)
34104
{
35105
try {
@@ -49,6 +119,11 @@ protected function getTableColumnsFromModel(EloquentModel $model)
49119
} catch (\Throwable $e) {
50120
}
51121

122+
try {
123+
return Schema::getColumns($model->getTable());
124+
} catch (\Throwable $e) {
125+
}
126+
52127
return [];
53128
}
54129

@@ -61,11 +136,18 @@ protected function getModelLabel(EloquentModel $model, string $label)
61136
if (config('erd-generator.use_db_schema')) {
62137
$columns = $this->getTableColumnsFromModel($model);
63138
foreach ($columns as $column) {
64-
$label = $column->getName();
139+
if (is_object($column)) {
140+
$name = $column->getName();
141+
$typeName = $column->getType()->getName();
142+
} else { // it's an array!
143+
$name = $column['name'] ?? '';
144+
$typeName = $column['type_name'] ?? '';
145+
}
146+
$label = $name;
65147
if (config('erd-generator.use_column_types')) {
66-
$label .= ' ('.$column->getType()->getName().')';
148+
$label .= ' ('. $typeName .')';
67149
}
68-
$table .= '<tr width="100%"><td port="' . $column->getName() . '" align="left" width="100%" bgcolor="'.config('erd-generator.table.row_background_color').'"><font color="'.config('erd-generator.table.row_font_color').'" >' . $label . '</font></td></tr>' . PHP_EOL;
150+
$table .= '<tr width="100%"><td port="' . $name . '" align="left" width="100%" bgcolor="'.config('erd-generator.table.row_background_color').'"><font color="'.config('erd-generator.table.row_font_color').'" >' . $label . '</font></td></tr>' . PHP_EOL;
69151
}
70152
}
71153

0 commit comments

Comments
 (0)