byrcsc/laravel-cartographer · 1.x
Introduction.
What Laravel Cartographer generates, where the diagram comes from, what it deliberately leaves out, and how to decide whether it fits your application.
Laravel Cartographer turns an existing Laravel application into a Mermaid entity relationship diagram. One Artisan command reads your Eloquent models and your database schema, then writes a Markdown file that renders directly on GitHub and GitLab.
php artisan cartographer:erdThe diagram lives in the repository, so a schema change shows up in the pull request diff next to the migration that caused it.
| Requirement | Supported versions |
|---|---|
| PHP | 8.2, 8.3, 8.4 |
| Laravel | 11.x, 12.x |
| Databases | MySQL, PostgreSQL, SQLite |
The package follows semantic versioning: upgrading within 1.x is safe. Source
and issues live at
github.com/byrcsc/laravel-cartographer.
The two sources
Cartographer never reads a row of your data. A diagram is assembled from two inputs, and every fact in it comes from one of them.
Your models supply the relationships. Cartographer instantiates each
discovered model and invokes its relation methods, so foreign keys, pivot
tables, and polymorphic names come out exactly as Eloquent resolves them. A
belongsTo with a custom foreign key is read from the relation object, not
guessed from a naming convention.
Your database supplies the structure. Tables, columns, types, primary keys, unique indexes, and foreign key constraints come from Laravel's native schema builder on a live connection. Because the structure is read from the database rather than parsed out of migration files, it stays correct on applications with squashed migrations, raw SQL migrations, and years of migration history.
What a diagram looks like
Relationships first, entities second:
erDiagram
posts }o--|| categories : "category"
posts ||--o{ comments : "commentable (morph)"
posts }o--o{ tags : "tags"
posts }o--|| users : "author"
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"
}Every list is sorted: tables, columns, and edges are emitted in the same order on every run. The committed file changes when your schema or your models change, and not otherwise.
What is included
- One Artisan command,
cartographer:erd, with options for seeds, depth, column detail, relation exclusions, format, output path, stdout, and connection. - Model discovery across configured directories, including glob patterns for modular and domain-driven layouts.
- Detection of
belongsTo,hasOne,hasMany,belongsToMany, everymorph*relation, and thethroughrelations, with an optional strict mode that only inspects methods carrying aRelationreturn type. - Column types normalized across MySQL, PostgreSQL, and SQLite, so the same application produces the same diagram on every driver.
- Seed models and a hop limit, for cutting a large schema down to one subsystem.
- Atomic file writing, so an interrupted run never leaves a truncated diagram behind.
- Markdown output with a
mermaidfence, or a raw.mmdfile formmdcand other Mermaid tooling.
What it does not do
The package draws its edges deliberately. What follows describes the shape it sets out to have. Treat none of it as planned work, and none of it as ruled out forever.
- Parse migration files. Migrations are history. The database structure is the single source of truth, which is why generating a diagram needs a reachable connection.
- Read table data. No query the package issues touches your rows; introspection reads structure only.
- Render PNG or SVG. GitHub, GitLab, and
mmdcalready render the output. - Support Laravel 10 or older. Those versions need
doctrine/dbalfor introspection, which would be a second permanent code path for end-of-life framework versions. - Support ORMs other than Eloquent.
- Add routes, pages, or anything browser-facing. Cartographer is an Artisan command that writes a file.
- Filter tables by name. Entities come from discovered models, plus the pivot tables their many-to-many relations name. A table with no model behind it never appears.
Design boundaries
Three decisions explain most of the behaviour you will meet.
Relations are resolved, not inferred. Cartographer builds the relation object and asks it for its keys. Building a relation runs no query, but it does mean each model is instantiated and its zero-argument public methods are called. See relationship detection for what that implies for models with side effects.
The database is authoritative for structure. A model whose table does not
exist is skipped with a warning rather than drawn from its $fillable. Key
markers come from real primary keys, unique indexes, and foreign key
constraints, so a schema that declares no foreign keys shows no FK markers.
Output is deterministic, or it is useless. A diagram that reorders itself on every run produces a noisy diff and stops being reviewable. Every collection the package builds is sorted before it is rendered, and the file is replaced through an atomic rename.
Where to go next
Continue with installation and setup, then follow the quick start to generate and commit a first diagram. From there, how a diagram is built explains the pipeline, and scoping a diagram covers cutting a large schema down to something readable.