laravel-er-diagram-generator
laravel-er-diagram-generator copied to clipboard
json output
I have an idea to build an interactive visualization of an ERD using d3. it would be awesome if we could get access to a raw json output of the relationships backing the svg generator.
it looks like phpDocumentor may already have support for that https://github.com/phpDocumentor/phpDocumentor2/pull/1446
I thought about the same thing. It would be great to get a JSON output somehow which can be used in D3js. Have you got it to work?
For those looking for a solution I created a parser in javascript which can take the SVG output and extract the data from the svg and return a json.
First output the .svg file to the public directory. In my case I output to the public\assets\images\erd.svg
using the command:
php artisan generate:erd public\assets\images\erd.svg --format=svg
Use the function erdSVGToJSON()
to return the JSON.
Note: If you saved your svg to a different file, edit the line: 'pathToSVG': 'assets/images/erd.svg'
in the settings
object to point to your svg file.
async function erdSVGToJSON() {
let settings = {
'pathToSVG': 'assets/images/erd.svg' // location of SVG file in public folder
}
let svgFile = await fetch(settings.pathToSVG)
.then(response => response.text())
.then(data => data);
const parser = new DOMParser();
const dom = parser.parseFromString(svgFile, "application/xml");
let tables = [...dom.querySelectorAll('.node')];
let relationships = [...dom.querySelectorAll('.edge')];
// format the tables
tables = tables.map(table => {
let texts = [...table.querySelectorAll('text')]
.map(text => text.textContent);
let [model, ...columns] = texts;
columns = columns.map(column => {
return {
columnName: column.replace(/\s*\(.*\)/g, ''),
columnType: column.substr(
column.indexOf('(') + 1,
column.indexOf(')') - column.indexOf('(') - 1
)
}
})
return { id: model.toLowerCase(), model, columns };
})
// format the relationships
relationships = relationships.map(relation => {
let texts = [...relation.querySelectorAll('title,text')].map(text => text.textContent);
let source = texts[0].split('->')[0].replace('appmodels', '');
let target = texts[0].split('->')[1].replace('appmodels', '');
let relationship = texts[2];
source = {
'table': source.split(':')[0],
'column': source.split(':')[1] ?? ''
}
target = {
'table': target.split(':')[0],
'column': target.split(':')[1] ?? ''
}
return {
source,
target,
relationship
}
})
return { tables, relationships };
};
Example of how to use it:
//example function to use the JSON
async function useJSON() {
jsonData = await erdSVGToJSON();
console.log(jsonData);
}
This returns a JS object with the following structure:
{
"tables": [
{
"id": "user",
"model": "User",
"columns": [
{
"columnName": "id",
"columnType": "bigint"
},
{
"columnName": "name",
"columnType": "string"
},
{
"columnName": "created_at",
"columnType": "datetime"
},
{
"columnName": "updated_at",
"columnType": "datetime"
}
]
},
{
"id": "role",
"model": "Role",
"columns": [
{
"columnName": "id",
"columnType": "bigint"
},
{
"columnName": "name",
"columnType": "string"
},
{
"columnName": "created_at",
"columnType": "datetime"
},
{
"columnName": "updated_at",
"columnType": "datetime"
}
]
}
],
"relationships": [
{
"source": {
"table": "user",
"column": "role_id"
},
"target": {
"table": "role",
"column": "id"
},
"relationship": "BelongsTo"
},
{
"source": {
"table": "role",
"column": "id"
},
"target": {
"table": "user",
"column": "role_id"
},
"relationship": "HasMany"
}
]
}