byrcsc/laravel-cartographer
Laravel Cartographer helps you map and visualize your Laravel application's structure. It gives a clear overview of routes, controllers, models, and their connections, making large codebases easier to navigate, debug, and refactor. Ideal for onboarding and understanding unfamiliar projects.
laravel-cartographer turns your Eloquent models into Mermaid ER diagrams that live in the repo next to the code. v1.2.0 is still a small package — the whole day-to-day surface is config plus two commands — so you can be productive in about ten minutes.
Install as a dev dependency. It only reads model metadata and writes docs, so it does not belong in production:
composer require --dev byrcsc/laravel-cartographer
php artisan vendor:publish --tag=cartographer-config # config/cartographer.php
Check your platform first: PHP 8.3+, Laravel 12 or 13, plus symfony/process ^7.2 || ^8.0. Composer will silently keep you on an older tag (or fail resolution) if you are on PHP 8.2 or Laravel 11 — that is deliberate, but it is the first thing to verify on CI images and older containers.
Read the config file before the docs. The release only mentions two keys by name — groups and themes — but everything you care about (model paths, output file, exclusions) is declared there. The published config is the real reference; https://docs.rcsc.dev/laravel-cartographer/v1/introduction fills in the semantics.
Generate once and commit the result.
php artisan cartographer:generate # writes docs/erd.md
git add docs/erd.md && git commit -m "docs: initial ERD"
First real use case: wire the drift check into CI immediately. The value of a committed diagram is only realised when it cannot silently rot.
# .github/workflows/docs.yml
- run: php artisan cartographer:check
It regenerates in memory, writes nothing, and exits 1 with a per-file diff when something is stale — safe on a read-only checkout with no build artefacts.
If you only do one of the v1.2.0 features on day one, make it cartographer:check.
One diagram of a 90-model app is unreadable and useless for review. Groups give each team the slice they talk about, committed as a sibling file: the main docs/erd.md produces docs/erd/billing.md, docs/erd/catalog.md, and so on.
// config/cartographer.php — illustrative shape, confirm key names against the published config
'groups' => [
'billing' => ['App\Models\Invoice', 'App\Models\Payment', 'App\Models\Subscription'],
'catalog' => ['App\Models\Product', 'App\Models\Category'],
],
php artisan cartographer:generate --group=billing regenerates just one file, which is the loop you want while refactoring a domain: iterate, eyeball the diff on docs/erd/billing.md, commit. Full runs regenerate all groups plus the main diagram.
Adopt groups when a single diagram passes roughly 20–30 models, or when two teams stop reading the same file.
cartographer:check is the centrepiece of this release. The useful pattern is not "check the diagram" but "check the diagram in the same job that checks code style", so a schema change and its diagram land in one commit:
- run: composer install --no-interaction --prefer-dist
- run: php artisan cartographer:check # exits 1 and prints the stale files + short diff
Because it lists each stale file rather than failing anonymously, the output is usually enough to tell a developer "you added a column and forgot composer erd" without opening the log twice.
--only and --except are for one-off exploration, not for committing narrower output. They deliberately leave the committed group files alone, so a run with --only=App\Models\Invoice will not rewrite docs/erd/billing.md:
# Quick look at a subtree while debugging a relation
php artisan cartographer:generate --only=App\Models\Order --format=svg
# Everything except a noisy legacy namespace
php artisan cartographer:generate --except=App\Models\Legacy
Good for local use, for a throwaway diagram in a PR description, or for a spike. Not a substitute for a group.
--format=svg|png renders through mermaid-cli, which you install yourself (node + @mermaid-js/mermaid-cli); it is intentionally not a package dependency. The important detail is that the Mermaid source is written beside the image, and that source — not the PNG — is what cartographer:check compares. So:
.mmd/source file is diffed.npm install -g @mermaid-js/mermaid-cli
php artisan cartographer:generate --format=svg --theme=dracula --font=mono
Keep image generation out of the default composer erd script unless everyone on the team has node; make it an opt-in --format flag so a plain run stays fast and dependency-free.
--theme=light|dracula (or a preset you define under themes) and --font=mono|sans|default apply to images only. Committed Markdown is deliberately theme-neutral, so there is no churn in .md diffs when someone changes presentation. Treat theming as a local/export concern and put your team's house style in themes in config so nobody retypes flags.
DiagramGenerator is now a public entry point and every command runs through it — which means anything a command can do (export, theming, limits) is available to you. Reasonable uses: a custom Artisan command that posts a diagram to a PR, a test asserting a group is non-empty, or a post-migration hook in local dev. Prefer calling the generator over shelling out to the artisan commands; you get the return values and exceptions instead of parsing console output.
Relationship detection is cached per model within a run, so drawing a model into the full diagram and into three of its groups costs one detection pass. If you are on v1.0.0 and running a full generation with several groups, this alone noticeably shortens the run. No configuration required.
cartographer:check can be green while your committed diagram is guaranteed to fail on GitHub's or Mermaid Live's hosted renderer. If you publish diagrams anywhere hosted, treat that warning as an error in your own CI (... | tee and grep for it), or keep the main diagram small and push detail into groups.check does not mean your images are current. It compares the Mermaid source. If you commit docs/erd.svg and the source drifts, the check still passes and the image is stale. Either regenerate images in the same step as the source, or treat images as build artefacts.check regenerates everything the config declares. Adding a group is a breaking change for CI in the good sense: the first run after adding a group fails until you commit the new file. Expect that on the PR that introduces the group, and commit the generated file in that same PR. The same applies after any intentional model change — the fix is to run cartographer:generate locally.--only/--except can hide drift. They don't touch committed group files, so a scoped run can leave a group stale while the developer believes they just regenerated. Always finish with a full cartographer:generate (or a check) before pushing.--group, cartographer:check and --format are v1.2.0+; if php artisan list shows no cartographer:check, run composer why-not byrcsc/laravel-cartographer and look at PHP/Laravel/symfony versions before reading the docs again.--theme. GitHub uses its own Mermaid theme; --theme/--font only affect exported images.themes in config for house styles; DiagramGenerator for anything scripted; the published config for paths/exclusions. There is no event or macro surface described in this release, so don't plan around one.docs/ as generated-but-reviewed. Reviewing the diagram diff is the whole point — it is a cheap way to catch an accidental belongsTo or a missing foreign key in code review. Don't add it to .gitattributes as binary or otherwise hide the diff.How can I help you explore Laravel packages today?