beyondcode/laravel-er-diagram-generator

json output

Closed this issue · 2 comments

Jonnx commented

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 phpDocumentor/phpDocumentor#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"
    }
  ]
}