# `Choreo.ERD`
[🔗](https://github.com/code-shoily/choreo/blob/v0.14.1/lib/choreo/erd.ex#L1)

Entity-Relationship Diagram (ERD) builder on top of Yog.

`Choreo.ERD` models database schemas, including tables, primary/foreign keys,
column definitions, and relations with standard cardinalities.

## Node types
* `:table` — represents a database table with structured columns.

## Edge types
* Relationships model foreign key links with standard multiplicities:
  * `:one_to_one` (exactly-one to exactly-one)
  * `:one_to_many` (exactly-one to zero-or-more)
  * `:zero_or_one_to_many` (zero-or-one to zero-or-more)
  * `:exactly_one_to_many` (exactly-one to one-or-more)
  * `:many_to_many` (zero-or-more to zero-or-more)

## Quick Start

    erd =
      Choreo.ERD.new()
      |> Choreo.ERD.add_table(:users, columns: [
        %{name: :id, type: :integer, key: :pk},
        %{name: :email, type: :varchar}
      ])
      |> Choreo.ERD.add_table(:posts, columns: [
        %{name: :id, type: :integer, key: :pk},
        %{name: :user_id, type: :integer, key: :fk},
        %{name: :title, type: :varchar}
      ])
      |> Choreo.ERD.add_relationship(:users, :posts, cardinality: :one_to_many, label: "writes")

    dot = Choreo.ERD.to_dot(erd)
    mermaid = Choreo.ERD.to_mermaid(erd)

## Diagram

<div class="graphviz">
  digraph G {
    graph [rankdir=LR, splines=spline, nodesep=1.2, ranksep=1.2];
    node [shape=plain, style=filled, fillcolor="transparent", fontname="Helvetica", fontsize=12, fontcolor="#1e293b"];
    edge [color="#64748b", style=solid, fontname="Helvetica", fontsize=10, penwidth=1.0];

    users [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="6" COLOR="#cbd5e1"><TR><TD COLSPAN="3" BGCOLOR="#3b82f6" ALIGN="CENTER" BORDER="1" COLOR="#cbd5e1"><FONT COLOR="#ffffff"><B>users</B></FONT></TD></TR><TR><TD ALIGN="LEFT" BORDER="1" COLOR="#cbd5e1"><FONT COLOR="#1e293b">id</FONT></TD><TD ALIGN="LEFT" BORDER="1" COLOR="#cbd5e1"><FONT COLOR="#1e293b"><i>integer</i></FONT></TD><TD ALIGN="CENTER" BORDER="1" COLOR="#cbd5e1"><FONT COLOR="#1e293b"><b>[PK]</b></FONT></TD></TR><TR><TD ALIGN="LEFT" BORDER="1" COLOR="#cbd5e1"><FONT COLOR="#1e293b">email</FONT></TD><TD ALIGN="LEFT" BORDER="1" COLOR="#cbd5e1"><FONT COLOR="#1e293b"><i>varchar</i></FONT></TD><TD ALIGN="CENTER" BORDER="1" COLOR="#cbd5e1"></TD></TR></TABLE>>];
    posts [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="6" COLOR="#cbd5e1"><TR><TD COLSPAN="3" BGCOLOR="#3b82f6" ALIGN="CENTER" BORDER="1" COLOR="#cbd5e1"><FONT COLOR="#ffffff"><B>posts</B></FONT></TD></TR><TR><TD ALIGN="LEFT" BORDER="1" COLOR="#cbd5e1"><FONT COLOR="#1e293b">id</FONT></TD><TD ALIGN="LEFT" BORDER="1" COLOR="#cbd5e1"><FONT COLOR="#1e293b"><i>integer</i></FONT></TD><TD ALIGN="CENTER" BORDER="1" COLOR="#cbd5e1"><FONT COLOR="#1e293b"><b>[PK]</b></FONT></TD></TR><TR><TD ALIGN="LEFT" BORDER="1" COLOR="#cbd5e1"><FONT COLOR="#1e293b">user_id</FONT></TD><TD ALIGN="LEFT" BORDER="1" COLOR="#cbd5e1"><FONT COLOR="#1e293b"><i>integer</i></FONT></TD><TD ALIGN="CENTER" BORDER="1" COLOR="#cbd5e1"><FONT COLOR="#1e293b"><i>[FK]</i></FONT></TD></TR><TR><TD ALIGN="LEFT" BORDER="1" COLOR="#cbd5e1"><FONT COLOR="#1e293b">title</FONT></TD><TD ALIGN="LEFT" BORDER="1" COLOR="#cbd5e1"><FONT COLOR="#1e293b"><i>varchar</i></FONT></TD><TD ALIGN="CENTER" BORDER="1" COLOR="#cbd5e1"></TD></TR></TABLE>>];

    users -> posts [penwidth="1.0", color="#64748b", dir="both", arrowhead="crowodot", arrowtail="teetee", label="writes"];
  }
</div>

# `t`

```elixir
@type t() :: %Choreo.ERD{
  edge_meta: %{optional(Yog.Multi.Graph.edge_id()) =&gt; map()},
  graph: Yog.Multi.Graph.t(),
  strict: boolean(),
  strict_column_matching: boolean()
}
```

# `table_id`

```elixir
@type table_id() :: Yog.node_id()
```

# `add_relationship`

```elixir
@spec add_relationship(t(), table_id(), table_id(), keyword()) :: t()
```

Adds a directed relationship/foreign key edge between two tables in the ERD.

## Options
* `:label` (`t:String.t/0`) - Visual label/description of the relationship (e.g. 'writes').

* `:cardinality` - Required. Multiplicity constraint: :one_to_one, :one_to_many, :zero_or_one_to_many, :exactly_one_to_many, or :many_to_many.

* `:from_column` (`t:term/0`) - The name of the column in the source table (atom or string).

* `:to_column` (`t:term/0`) - The name of the column in the destination table (atom or string).

## Examples

    iex> erd = Choreo.ERD.new()
    ...> |> Choreo.ERD.add_table(:users, columns: [%{name: :id, type: :integer}])
    ...> |> Choreo.ERD.add_table(:posts, columns: [%{name: :user_id, type: :integer}])
    ...> |> Choreo.ERD.add_relationship(:users, :posts, cardinality: :one_to_many)
    iex> %Choreo.ERD{} = erd
    iex> map_size(erd.graph.edges)
    1

# `add_table`

```elixir
@spec add_table(t(), table_id(), keyword()) :: t()
```

Adds a table to the ERD diagram with structured columns.

## Options
* `:label` (`t:String.t/0`) - Visual label for the table. Defaults to stringified table ID.

* `:columns` (`t:term/0`) - Required. List of structured columns.

## Examples

    iex> erd = Choreo.ERD.new()
    ...> |> Choreo.ERD.add_table(:users, columns: [%{name: :id, type: :integer, key: :pk}])
    iex> %Choreo.ERD{} = erd
    iex> Map.has_key?(erd.graph.nodes, :users)
    true

# `new`

```elixir
@spec new(keyword()) :: t()
```

Initializes a new, empty ERD.

## Options

  * `:strict_column_matching` - if `true`, validates that any foreign key columns mapped between tables exist and have matching types (default: `false`).
  * `:strict` - if `true`, `add_relationship/4` raises when an endpoint does not already exist (default: `false`).

## Examples

    iex> erd = Choreo.ERD.new()
    iex> %Choreo.ERD{} = erd
    iex> erd.strict_column_matching
    false

# `theme`

```elixir
@spec theme(atom(), keyword()) :: Choreo.Theme.t()
```

Returns a theme for the ERD diagram.

## Examples

    iex> theme = Choreo.ERD.theme(:dark)
    iex> theme.graph_bgcolor
    "#0f172a"

# `to_dot`

```elixir
@spec to_dot(t(), keyword()) :: String.t()
```

Renders the ERD to Graphviz DOT syntax.

## Examples

    iex> erd = Choreo.ERD.new() |> Choreo.ERD.add_table(:users, columns: [%{name: :id, type: :integer}])
    iex> dot = Choreo.ERD.to_dot(erd)
    iex> dot =~ "digraph"
    true

# `to_mermaid`

```elixir
@spec to_mermaid(t(), keyword()) :: String.t()
```

Renders the ERD to Mermaid.js native erDiagram syntax.

## Examples

    iex> erd = Choreo.ERD.new() |> Choreo.ERD.add_table(:users, columns: [%{name: :id, type: :integer}])
    iex> Choreo.ERD.to_mermaid(erd) =~ "erDiagram"
    true

---

*Consult [api-reference.md](api-reference.md) for complete listing*
