byrcsc/laravel-cartographer · 1.x
Quick start.
Generate a full diagram for a blog application, read what came out, scope it down to one subsystem, and commit both files.
This walkthrough generates two diagrams for a small blog application: a full one for the repository, and a focused one for the publishing subsystem. It assumes the package is installed and the database is migrated.
The example application has eight models under app/Models: User, Profile,
Category, Post, Comment, Tag, Attachment, and AuditLog. Every output
below is what those models and their schema actually produce.
1. Generate the full diagram
php artisan cartographer:erd INFO Generated 9 entities and 18 edges. Written to [/srv/blog/docs/erd.md].Nine entities from eight models: the extra one is post_tag, the pivot table
behind Post::tags(). Pivot tables are pulled in because the diagram would
otherwise hide a real table.
Open docs/erd.md and GitHub renders it as a diagram. The file starts with a
generation marker and a mermaid fence:
<!-- Generated by byrcsc/laravel-cartographer. -->
```mermaid
erDiagram
attachments }o--|| comments : "attachable (morph)"
attachments }o--|| posts : "attachable (morph)"
categories }o--|| categories : "parent"
categories ||--o{ categories : "children"
categories ||--o{ posts : "posts"
comments ||--o{ attachments : "attachable (morph)"
comments }o--|| posts : "post"
comments }o--|| users : "author"
posts ||--o{ attachments : "attachable (morph)"
posts }o--|| categories : "category"
posts ||--o{ comments : "comments"
posts }o--o{ tags : "tags"
posts }o--|| users : "author"
profiles }o--|| users : "user"
tags }o--o{ posts : "posts"
users ||--o{ comments : "comments"
users ||--o{ posts : "posts"
users ||--o| profiles : "profile"
```Four things in that block are worth reading closely.
posts }o--|| users : "author" is labelled with the relation method name,
not the table name. Post::author() points at User through author_id, and
the label says author because that is what the code calls it.
categories }o--|| categories : "parent" is a self-reference. It is drawn like
any other edge.
attachable (morph) appears on both sides of the polymorphic relation. The
label is the morph name, and Attachment::attachable() produces one edge per
model that declares the inverse — here Post and Comment.
Both directions of a relation appear when both sides declare it. posts ||--o{ comments : "comments" and comments }o--|| posts : "post" are the same
foreign key, seen from each model. Cartographer does not collapse them, because
one side going missing is usually worth noticing.
2. Read the entity blocks
Below the edges, each entity lists its columns:
posts {
bigint author_id FK
text body "nullable"
bigint category_id FK "nullable"
timestamp created_at "nullable"
bigint id PK
timestamp published_at "nullable"
varchar title
timestamp updated_at "nullable"
}Columns are sorted by name, not by their position in the table. Types are
normalized, so bigint means the same thing whether the database is MySQL,
PostgreSQL, or SQLite.
PK, FK, and UK come from the database, not from the models: primary keys,
foreign key constraints, and unique indexes. A schema with no foreign key
constraints produces no FK markers even when the relations are correct.
3. Cut it down to one subsystem
The full diagram is the reference. For a page about publishing, start from
Post and follow one hop:
php artisan cartographer:erd \
--models=Post \
--depth=1 \
--columns=keys \
--output=docs/erd-publishing.md INFO Generated 7 entities and 16 edges. Written to [/srv/blog/docs/erd-publishing.md].Seven entities instead of nine. profiles is two hops away through User, and
audit_logs has no relations at all, so neither is included.
--columns=keys drops everything that is not a primary key, a foreign key, or
unique:
posts {
bigint author_id FK
bigint category_id FK
bigint id PK
}
tags {
bigint id PK
varchar name UK
}One detail that surprises people: posts.category_id still carries its FK
marker even in a scope where categories was excluded. Columns describe the
table, not the diagram.
4. Choose how much detail to show
Three column modes, from most to least detail:
| Mode | Shows | Good for |
|---|---|---|
all | Every column, with "nullable" where it applies | The committed reference diagram |
keys | Primary, foreign, and unique columns only | Subsystem diagrams |
none | Entity names and edges only | A map of the relationships |
none is the one to reach for when the question is "what talks to what":
erDiagram
categories ||--o{ posts : "posts"
comments }o--|| posts : "post"
posts }o--o{ tags : "tags"
posts }o--|| users : "author"
categories
comments
posts
tags
users5. Preview before writing
--stdout prints the diagram instead of writing a file, and suppresses the
warnings and the summary line so the output pipes cleanly:
php artisan cartographer:erd --models=Post --depth=1 --stdout
php artisan cartographer:erd --stdout --format=mmd > /tmp/erd.mmdUse --format=mmd for a raw Mermaid file with no Markdown wrapper. That is the
input mmdc and most Mermaid tooling expects.
6. Commit both files
git add docs/erd.md docs/erd-publishing.md
git commit -m "docs: add generated ERDs"Both files are deterministic. Regenerate without changing the application and
git status stays clean, which is what makes the next step useful.
7. Regenerate after a migration
php artisan migrate
php artisan cartographer:erd
git diff docs/erd.mdThe diff shows exactly what the migration did to the diagram — a new column, a new edge, a changed type. That diff belongs in the same pull request as the migration.
To stop having to remember, see keeping the diagram current for a Composer script and a git hook, and continuous integration for a build that fails when the committed diagram is stale.
What to read next
- How a diagram is built for the five stages and where each fact comes from.
- Scoping a diagram for seeds, depth, and relation exclusions in detail.
- Diagrams per subsystem for generating a set of them in one go.
- Configuration to make today's flags tomorrow's defaults.