Skip to content

Commit f05eb01

Browse files
authored
Merge pull request #120 from mathijso/master
Add structured text output for AI model context
2 parents dd92522 + 20687c6 commit f05eb01

File tree

4 files changed

+227
-6
lines changed

4 files changed

+227
-6
lines changed

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.

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: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,68 @@ public function buildGraph(Collection $models) : Graph
3030
return $this->graph;
3131
}
3232

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

tests/GenerationTest.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,88 @@ public function it_generated_graphviz_in_jpeg_format()
5858

5959
$this->assertStringContainsString('Wrote diagram to graph.jpeg', Artisan::output());
6060
}
61+
62+
/** @test */
63+
public function it_generates_text_output_file_with_text_output_option()
64+
{
65+
$this->app['config']->set('erd-generator.directories', [__DIR__ . '/Models']);
66+
67+
$outputFile = __DIR__ . '/output_test.txt';
68+
69+
// Make sure the file doesn't exist before the test
70+
if (file_exists($outputFile)) {
71+
unlink($outputFile);
72+
}
73+
74+
Artisan::call('generate:erd', [
75+
'filename' => $outputFile,
76+
'--text-output' => true
77+
]);
78+
79+
$this->assertFileExists($outputFile);
80+
$this->assertStringContainsString('Wrote text diagram to ' . $outputFile, Artisan::output());
81+
82+
// Check if the file contains GraphViz DOT content
83+
$fileContent = file_get_contents($outputFile);
84+
$this->assertStringContainsString('digraph', $fileContent);
85+
86+
// Clean up
87+
if (file_exists($outputFile)) {
88+
unlink($outputFile);
89+
}
90+
}
91+
92+
/** @test */
93+
public function it_generates_structured_text_output_for_txt_extension()
94+
{
95+
$this->app['config']->set('erd-generator.directories', [__DIR__ . '/Models']);
96+
97+
$outputFile = __DIR__ . '/structured_test.txt';
98+
99+
// Make sure the file doesn't exist before the test
100+
if (file_exists($outputFile)) {
101+
unlink($outputFile);
102+
}
103+
104+
Artisan::call('generate:erd', [
105+
'filename' => $outputFile
106+
]);
107+
108+
$this->assertFileExists($outputFile);
109+
$this->assertStringContainsString('Wrote structured ER diagram to ' . $outputFile, Artisan::output());
110+
111+
// Check if the file contains structured Markdown content
112+
$fileContent = file_get_contents($outputFile);
113+
$this->assertStringContainsString('# Entity Relationship Diagram', $fileContent);
114+
$this->assertStringContainsString('## Entities', $fileContent);
115+
$this->assertStringContainsString('## Relationships', $fileContent);
116+
117+
// Clean up
118+
if (file_exists($outputFile)) {
119+
unlink($outputFile);
120+
}
121+
}
122+
123+
/** @test */
124+
public function it_generates_structured_text_output_with_correct_content()
125+
{
126+
$this->app['config']->set('erd-generator.directories', [__DIR__ . '/Models']);
127+
128+
// Get the structured text output directly from the GraphBuilder
129+
$models = $this->app->make('BeyondCode\ErdGenerator\ModelFinder')
130+
->getModelsInDirectory(__DIR__ . '/Models')
131+
->transform(function ($model) {
132+
return new \BeyondCode\ErdGenerator\Model(
133+
$model,
134+
(new \ReflectionClass($model))->getShortName(),
135+
$this->app->make('BeyondCode\ErdGenerator\RelationFinder')->getModelRelations($model)
136+
);
137+
});
138+
139+
$structuredOutput = $this->app->make('BeyondCode\ErdGenerator\GraphBuilder')
140+
->generateStructuredTextRepresentation($models);
141+
142+
// Assert the structured output matches the snapshot
143+
$this->assertMatchesSnapshot($structuredOutput);
144+
}
61145
}

0 commit comments

Comments
 (0)