Browse documentationOpen

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:

  1. Relation exclusions remove edges before anything else runs.
  2. Seeds name where the diagram starts.
  3. 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            # unlimited

For the blog application in the quick start:

CommandEntitiesWhat is included
--models=Post --depth=01posts alone, with no edges
--models=Post --depth=17Direct neighbours: users, categories, comments, tags, attachments, plus the post_tag pivot
--models=Post --depth=28Adds profiles, reached through users
--models=Post8Everything connected to posts; audit_logs has no relations, so it stays out
(no seeds)9Every 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:

ValueRemoves
throughhas_one_through and has_many_through
morphmorph_to, morph_one, morph_many, morph_to_many, morphed_by_many
An exact type nameOnly 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.md

Read 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.