byrcsc/laravel-cartographer · 1.x
Scoping a diagram.
Cut a large schema down with seed models, a hop limit, and relation type exclusions, and understand which entities and edges survive each.
A full diagram of a mature application is a wall. Scoping produces the diagram someone will actually read: a subsystem, its neighbours, and nothing else.
Three controls, applied in this order:
- Relation exclusions remove edges before anything else runs.
- Seeds name where the diagram starts.
- Depth limits how far it spreads from the seeds.
Order matters. Excluding a relation type can disconnect a model, which then falls outside the depth limit and disappears — that is intended, and it is how you drop a subsystem that hangs off a single polymorphic relation.
Seeds
--models names one or more starting models by short class name or fully
qualified name:
php artisan cartographer:erd --models=Post
php artisan cartographer:erd --models=Post,Invoice
php artisan cartographer:erd --models='App\Models\Post'Quote the fully qualified form; the backslashes need protecting from the shell.
With no seeds, every discovered model with a table is included and the diagram is the whole application.
Seeds resolve against discovered models. Three failures are possible, and each ends the command:
ERROR Seed model [Postt] was not found. Did you mean: Post, User, Comment?Up to three near matches are offered, ranked by edit distance.
ERROR Seed model [Order] is ambiguous. Use one of: App\Models\Order, App\Domain\Sales\Models\Order.Two discovered models share a short name. Pass the fully qualified one.
ERROR Seed model [Ghost] has no available database table.The class was discovered, but its table does not exist on the connection, so it never became an entity. This is a different message from "not found" on purpose: the problem is the database, not the spelling.
There is no config key for seeds. Seeding is a per-run decision, so it lives on the command line.
Depth
--depth is the number of relationship hops to follow outward from the seeds.
It counts hops, and it ignores edge direction — an edge is traversable from
either end.
php artisan cartographer:erd --models=Post --depth=0
php artisan cartographer:erd --models=Post --depth=1
php artisan cartographer:erd --models=Post # unlimitedFor the blog application in the quick start:
| Command | Entities | What is included |
|---|---|---|
--models=Post --depth=0 | 1 | posts alone, with no edges |
--models=Post --depth=1 | 7 | Direct neighbours: users, categories, comments, tags, attachments, plus the post_tag pivot |
--models=Post --depth=2 | 8 | Adds profiles, reached through users |
--models=Post | 8 | Everything connected to posts; audit_logs has no relations, so it stays out |
| (no seeds) | 9 | Every model, connected or not |
--depth on its own, with no --models, does nothing. There is no seed to
measure hops from, so the full graph is produced. If a depth-limited diagram
comes back complete, the missing flag is --models.
Edges between neighbours are kept
Selection picks entities; edges are then filtered to those with both ends
selected. So a --depth=1 diagram includes edges between two neighbours of the
seed, not only edges touching the seed itself.
Seeding on User at depth 1 keeps comments }o--|| posts : "post", because
both comments and posts are neighbours of users. This is what makes a
shallow diagram readable: the neighbourhood is drawn as it is, not as a star.
Columns describe the table, not the scope
A column keeps its FK marker even when the table it references is out of
scope:
posts {
bigint author_id FK
bigint category_id FK
bigint id PK
}Here categories was not selected, and category_id FK is still correct: the
constraint exists in the database. Treat a marker with no matching edge as a
sign that the diagram is scoped, not that something is broken.
Excluding relation types
--exclude-relations takes a comma-separated list of type names:
php artisan cartographer:erd --exclude-relations=through
php artisan cartographer:erd --exclude-relations=morph,belongs_to_many// config/cartographer.php
'relations' => [
'exclude' => ['through'],
],Values match against the type names in relationship detection, plus two group aliases:
| Value | Removes |
|---|---|
through | has_one_through and has_many_through |
morph | morph_to, morph_one, morph_many, morph_to_many, morphed_by_many |
| An exact type name | Only that type — belongs_to_many, morph_many, has_one, and so on |
Matching is forgiving about formatting: values are trimmed, lowercased, and
hyphens and spaces become underscores, so Belongs-To-Many and
belongs_to_many are the same thing.
through relations are the usual first exclusion. They are shortcuts across an
intermediate model, so they add an edge the diagram already implies through two
others.
One asymmetry to know about: excluding morph_many on its own removes those
edges but leaves the morph_to edges pointing back, because the inverse
relations are read before exclusions are applied. Exclude morph when you want
the family gone.
The option replaces the config, it does not add to it
Passing --exclude-relations ignores relations.exclude entirely, rather than
merging with it. Passing --exclude-relations= with an empty value clears the
configured exclusions for that run.
Pivot tables
A many-to-many relation draws its edge directly between the two models:
posts }o--o{ tags : "tags"The pivot table is still added as an entity, with its columns, whenever the edge is in scope and the table exists:
post_tag {
bigint id PK
bigint post_id FK, UK
bigint tag_id FK, UK
}It has no edges of its own. This is deliberate: a pivot carrying extra columns —
sort_order, added_by, timestamps — is real schema, and hiding it would make
the diagram lie about the table count. A pivot whose edge is scoped out is
dropped with the edge.
Pivot entities never carry a model class, so columns.exclude cannot hide
columns on them.
Composing a scope
The three controls combine:
php artisan cartographer:erd \
--models=Post,Category \
--depth=1 \
--exclude-relations=through,morph \
--columns=keys \
--output=docs/erd-publishing.mdRead it as: start at Post and Category, follow one hop, ignore through and
polymorphic relations entirely, show key columns only, write it beside the full
diagram.
What to read next
- Diagrams per subsystem for generating a set of scoped diagrams in one command.
- Console commands for the full option reference.
- Diagram syntax for what the rendered output means.