Atelier

ER Diagrams

An entity-relationship diagram is a set of entities with typed attribute rows and the labeled relationships between them, each end carrying a cardinality.

CUSTOMERidintnamestringemailstringORDERidintplacedAtdatetotaldecimalORDER_ITEMquantityintunitPricedecimalPRODUCTidintskustringnamestringPAYMENTidintamountdecimalSHIPMENTidinttrackingNumberstring||o{places|||{contains||o{appears in||o{paid by|||oships as

Overview

The model lives in Atelier\Diagram\Er\: ErDiagram, ErEntity (id, list of attributes), ErAttribute (type, name), ErRelationship (from, fromCardinality, to, toCardinality, optional label), and the ErCardinality enum. All model classes are immutable.

Reach for it to model a data schema: tables and their columns, and how records relate (one customer places many orders). Use a class diagram instead when you are modeling object types with methods and inheritance rather than data entities and their cardinalities.

Format

erDiagram
    ORDER {
        int id
        string status
    }
    CUSTOMER ||--o{ ORDER : places

Entity and attribute names match [A-Za-z_][A-Za-z0-9_]*; attribute types are single non-space tokens. Cardinality tokens are |o (zero or one), || (exactly one), o{ (zero or more), and |{ (one or more). Entities referenced by a relationship are auto-declared in first-use order. Full grammar: Mermaid support.

Builder API

ErDiagramBuilder (or Diagram::er(), which returns it) is the one way to build the model:

use Atelier\Diagram\Diagram;

$diagram = Diagram::er()
    ->attribute('CUSTOMER', 'string', 'name')
    ->attribute('CUSTOMER', 'string', 'email')
    ->attribute('ORDER', 'int', 'id')
    ->attribute('ORDER', 'string', 'status')
    ->relationship('CUSTOMER', '||', 'ORDER', 'o{', 'places')
    ->build();

Diagram::of($diagram)->saveSvg('schema.svg');
  • entity($id) - declares an entity with no attributes; a no-op if it already exists. Throws InvalidArgumentException on an empty id.
  • attribute($entityId, $type, $name) - appends a type name row, auto-declaring the entity on first use. Rows keep insertion order.
  • relationship($from, $fromCardinality, $to, $toCardinality, $label = null) - adds a relationship, auto-declaring both endpoints. Cardinalities accept a token string ('||', 'o{', ...) or an ErCardinality case; an unknown token throws InvalidArgumentException.
  • build() - assembles the ErDiagram in entity declaration order.

Options

Setting Default Effect
attribute(...) none a name column and a type column inside the entity box
Theme::spacingUnit per preset entity width floor of 15 * spacingUnit, padding, and gaps
  • Relationship labels: a label widens the routing corridor so the text over its background halo does not collide with the connectors.
  • Determinism: layout is source-order based and deterministic; the same model always yields the same SVG.

Layout\Er\ErLayoutEngine places entity boxes in a compact grid, then routes relationships through atelier/layout orthogonal routing. Cardinality tokens render as short bold text near each endpoint over a light background rather than crow-foot glyphs.

Themes

ER diagrams use nodeFillColor and nodeStrokeColor for entity boxes and connectors, textColor for attribute names, and mutedTextColor for attribute types. The entity header bar is filled with accentColors[0] with white title text, so only the first accent color matters; the palette size does not change the rendering.

All presets apply (Theme::default(), dark(), blueprint(), mono(), neutral()), and a Scene\BackgroundPattern (as in blueprint()) sits behind the entities. See Theming.

CUSTOMERidintnamestringemailstringORDERidintplacedAtdatetotaldecimalORDER_ITEMquantityintunitPricedecimalPRODUCTidintskustringnamestringPAYMENTidintamountdecimalSHIPMENTidinttrackingNumberstring||o{places|||{contains||o{appears in||o{paid by|||oships as

Theme::blueprint() on the same entities. Presets are passed at render time, so one model can serve print, screen, and slide decks.

Parse

Header keyword: erDiagram.

$diagram = Diagram::fromMermaid($source);        // throws ParseException
$result  = Diagram::tryFromMermaid($source);      // non-throwing ParseResult

Canonical output writes explicit entity blocks even for auto-declared entities. Identifying-relation variants beyond --, quoted attribute names, keys, comments, composite attributes, aliases, and styling are rejected, never skipped. Full grammar and round-trip rules: Mermaid support.

Debug

On a rejected source, inspect ParseResult::getDiagnostic() (a ParserDiagnostic with a message, a stable code, and a source span) or catch ParseException (getLineNumber(), getSourceLine()). Render a code frame with ParserDiagnosticFormatter:

$result = Diagram::tryFromMermaid($source);
if ($result->isFailure()) {
    echo \Atelier\Diagram\Parser\Support\ParserDiagnosticFormatter::format($result->getDiagnostic());
}

See parser diagnostics.