›
›
›
  1. docs
  2. ›
  3. byrcsc/laravel-cartographer
1.x
Browse documentationOpenClose

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • How a diagram is built
  • Model discovery
  • Relationship detection
  • Schema introspection
  • Scoping a diagram

Operations

  • Diagrams per subsystem
  • Exporting images
  • Themes and fonts
  • Renderer limits
  • Keeping the diagram current
  • Continuous integration

Reference

  • Configuration
  • Console commands
  • Diagram syntax
  • PHP API
  • Testing
  • Troubleshooting

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • How a diagram is built
  • Model discovery
  • Relationship detection
  • Schema introspection
  • Scoping a diagram

Operations

  • Diagrams per subsystem
  • Exporting images
  • Themes and fonts
  • Renderer limits
  • Keeping the diagram current
  • Continuous integration

Reference

  • Configuration
  • Console commands
  • Diagram syntax
  • PHP API
  • Testing
  • Troubleshooting

byrcsc/laravel-cartographer · 1.x

Scoping a diagram.

Limit a diagram to the models and relationships needed for one part of your application.

A full application diagram can contain too many tables to read at once. Scoping creates a smaller diagram for one subsystem and its nearby models.

Cartographer applies five controls in this order:

  1. --except removes models from the run before anything is read.
  2. Relation exclusions remove edges next.
  3. Seeds name where the diagram starts.
  4. Depth limits how far it spreads from the seeds.
  5. --only narrows the finished diagram to a set, stubbing what it cuts.

Order matters. Excluding a relation type can disconnect a model. The model can then fall outside the depth limit and disappear from the diagram.

The two ends of that list are the ones people confuse. --except and --only both narrow the model set, and they do it in opposite directions and leave different traces. The scoping a set versus removing models section below is the short version if that is what you came for.

For a scope you intend to commit, declare a group instead of remembering flags. Everything on this page describes a single run.

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 sets the number of relationship steps to follow from the seeds. Edge direction does not affect the count, so Cartographer can follow an edge 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, such as belongs_to_many, morph_many, or has_one

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.

Scoping a set versus removing models

--only and --except both take a comma-separated list of models, in short or fully qualified form, and they are not opposites.

php artisan cartographer:erd --only=Order,Invoice
php artisan cartographer:erd --except=Telemetry,AuditLog
--only--except
NamesWhat to keepWhat to drop
A relation leaving the setKeeps its edge, far entity stubbedGone with the model
Relationship to exclude_modelsIndependentComposes with it
Equivalent toA group, unnamedA one-run exclude_models

--only scopes the diagram the way a group does. A model outside the set that something inside points at is drawn as a stub: a bare entity name, no columns, no edges of its own.

erDiagram
    invoices }o--|| users : "user"

    invoices {
        bigint id PK
        bigint user_id FK
    }
    users

That is the point of --only. The diagram stays honest about its boundaries without dragging in everything on the other side of them.

--except removes the models outright. Their entities and every edge touching them are gone, and nothing is stubbed, because the model was never part of the run to begin with. It composes with exclude_models rather than replacing it, so a model named in either place is out.

Removing everything fails rather than writing an empty diagram:

   ERROR  Every discovered model was excluded. Check [--except] and [cartographer.exclude_models].

Which to reach for

Use --except for noise: token tables, job tables, audit logs, telemetry. Things that are real schema and never what anyone opened the diagram to look at.

Use --only for a subsystem, when the boundary itself is information. Then promote it to a group once you want the file committed, because a group regenerates on every run and a remembered flag does not.

Either one narrows the run to one diagram

Passing --only or --except, like passing --models, --depth, --output, or --stdout, produces that one diagram and leaves the configured group files untouched. An experiment cannot rewrite your committed set.

--group is the exception, and it refuses to be narrowed further:

   ERROR  A group is already a scope, so [--group] cannot be combined with
          [--only], [--except], [--models] or [--depth].

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. A pivot can still contain important columns such as sort_order, added_by, or timestamps. Showing the pivot keeps those columns and the table count visible. A pivot is removed when its relationship falls outside the scope.

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 \
  --except=AuditLog \
  --columns=keys \
  --output=docs/erd-publishing.md

Read it as: drop AuditLog from the run, ignore through and polymorphic relations entirely, start at Post and Category, follow one hop, show key columns only, write it beside the full diagram.

--only is the one that does not compose usefully with the rest. It names the final set outright, so combining it with seeds and depth means two answers to the same question. Reach for one or the other.

What to read next

  • Diagrams per subsystem for promoting a scope to a committed group.
  • Console commands for the full option reference.
  • Diagram syntax for what the rendered output means.
PreviousSchema introspectionNextDiagrams per subsystem
View source

On this page

  1. Seeds
  2. Depth
  3. Edges between neighbours are kept
  4. Columns describe the table, not the scope
  5. Excluding relation types
  6. The option replaces the config, it does not add to it
  7. Scoping a set versus removing models
  8. Which to reach for
  9. Either one narrows the run to one diagram
  10. Pivot tables
  11. Composing a scope
  12. What to read next