PHP Classes

File: resources/js/mermaid-definition.js

Recommend this page to a friend!
  Packages of Alberto Arena   Laravel Truss   resources/js/mermaid-definition.js   Download  
File: resources/js/mermaid-definition.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Laravel Truss
View a diagram of a Laravel application models
Author: By
Last change:
Date: 9 days ago
Size: 3,870 bytes
 

Contents

Class file image Download
// Turns a selected subset of schema tables into a Mermaid `erDiagram` string. // This is the shared sink of the selection pipeline: config exclusions, text // filter, and focus mode all reduce to a table subset that arrives here. // // Per-column PK/FK badges are DERIVED here from `primary_key` and // `foreign_keys[].columns` ? they are not stored on columns (see docs/DESIGN.md). import { toShortLabel } from './type-labels.js'; /** * Collapse a native type into a single Mermaid-safe attribute-type token. * Mermaid's ER grammar splits attributes on whitespace, so `bigint unsigned` * and `varchar(255)` must become one word: `bigint_unsigned`, `varchar_255`. * * `enum(...)`/`set(...)` value lists can be enormous and blow out the column * width, so in native mode they collapse to just the keyword; the full list is * surfaced on hover by the presentation layer (see truss.js). `laravel` mode * already reduces them to `enum` via toShortLabel. */ function mermaidType(nativeType, mode) { const label = mode === 'laravel' ? toShortLabel(nativeType) : String(nativeType).replace(/^\s*(enum|set)\b[\s\S]*$/i, '$1'); return String(label) .trim() .replace(/[^A-Za-z0-9]+/g, '_') .replace(/^_+|_+$/g, '') || 'unknown'; } /** * The PK/FK badge for a column, or '' when it is neither. */ function keyBadge(columnName, primaryKey, foreignKeyColumns) { const badges = []; if (primaryKey.includes(columnName)) { badges.push('PK'); } if (foreignKeyColumns.has(columnName)) { badges.push('FK'); } return badges.join(', '); } function entityBlock(table, mode) { const primaryKey = table.primary_key ?? []; const foreignKeys = table.foreign_keys ?? []; const fkColumns = new Set(foreignKeys.flatMap((fk) => fk.columns)); // Columns whose FK points back at this same table. Mermaid renders self-loops // as a large sweeping curve, so instead of an edge we flag the column with a // "self-ref" note (see generateErDiagram, where the self-loop edge is skipped). const selfRefColumns = new Set( foreignKeys.filter((fk) => fk.references_table === table.name).flatMap((fk) => fk.columns), ); const lines = table.columns.map((column) => { const type = mermaidType(column.type, mode); const badge = keyBadge(column.name, primaryKey, fkColumns); const note = selfRefColumns.has(column.name) ? ' "self-ref"' : ''; return ` ${type} ${column.name}${badge ? ` ${badge}` : ''}${note}`.trimEnd(); }); return [` ${table.name} {`, ...lines, ' }'].join('\n'); } /** * @param {Array} tables the selected subset (already filtered/focused) * @param {{ typeLabels?: 'native' | 'laravel' }} [options] * @returns {string} a Mermaid erDiagram definition */ export function generateErDiagram(tables, options = {}) { const mode = options.typeLabels === 'laravel' ? 'laravel' : 'native'; const present = new Set(tables.map((table) => table.name)); const entities = tables.map((table) => entityBlock(table, mode)); // Only emit an edge when BOTH endpoints are in the subset, so focus/filter // never conjure a phantom entity for an out-of-scope referenced table. const relationships = []; for (const table of tables) { for (const fk of table.foreign_keys ?? []) { if (!present.has(fk.references_table)) { continue; } if (fk.references_table === table.name) { // Self-reference: shown as a "self-ref" column note (see entityBlock), // not a self-loop edge, which Mermaid draws as a large sweeping curve. continue; } const label = fk.name || fk.columns.join('_'); // parent ||--o{ child : "constraint" (child is the referencing table). relationships.push(` ${fk.references_table} ||--o{ ${table.name} : "${label}"`); } } return ['erDiagram', ...entities, ...relationships].join('\n'); }