byrcsc/laravel-cartographer

Generate a Mermaid entity relationship diagram of your Laravel application from its Eloquent models and database schema.

Maintainers

Package info

github.com/byrcsc/laravel-cartographer

Forum

Documentation

pkg:composer/byrcsc/laravel-cartographer

Transparency log

Fund package maintenance!

Buy Me A Coffee

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-08-04 09:23 UTC

This package is auto-updated.

Last update: 2026-08-05 06:31:17 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub PHPStan Action Status Total Downloads

Generate a Mermaid entity relationship diagram of your Laravel application from its Eloquent models and database schema. Every app eventually needs one: onboarding a developer, planning a migration, working out what still points at a table. Drawn by hand, it is wrong within a month.

The package provides the diagram generator. Your application keeps ownership of its models, its schema, and where the output file lives. Cartographer never reads your data.

Laravel Tested PHP versions
12.x 8.3, 8.4
13.x 8.3, 8.4

Tested on MySQL, PostgreSQL, and SQLite.

One artisan command writes a Markdown file that GitHub and GitLab render on sight. Commit it, and each schema change shows up as a diff in the pull request that caused it.

php artisan cartographer:erd
erDiagram
    categories }o--|| categories : "parent"
    categories ||--o{ categories : "children"
    categories ||--o{ posts : "posts"
    comments }o--|| posts : "commentable (morph)"
    comments }o--|| users : "author"
    comments }o--|| users : "commentable (morph)"
    documents }o--|| users : "owner"
    posts }o--|| categories : "category"
    posts ||--o{ comments : "commentable (morph)"
    posts ||--o| comments : "commentable (morph)"
    posts }o--o{ tags : "tags"
    posts }o--o{ tags : "taggable (morph)"
    posts }o--|| users : "author"
    tags }o--o{ posts : "posts"
    tags }o--o{ posts : "taggable (morph)"
    telemetry_events }o--|| users : "alternateUser"
    telemetry_events }o--|| users : "untypedUser"
    telemetry_events }o--|| users : "user"
    users ||--o{ comments : "postComments (through)"
    users ||--o| comments : "firstPostComment (through)"
    users ||--o{ comments : "commentable (morph)"
    users ||--o{ documents : "documents"
    users ||--o| documents : "primaryDocument"
    users ||--o{ posts : "posts"

    categories {
        bigint id PK
        bigint parent_id FK
    }
    comments {
        bigint author_id FK
        bigint id PK
    }
    documents {
        varchar id PK
        bigint owner_id FK
    }
    post_tag {
        bigint id PK
        bigint post_id FK, UK
        bigint tag_id FK, UK
    }
    posts {
        bigint author_id FK
        bigint category_id FK
        bigint id PK
    }
    taggables {
        bigint tag_id FK, UK
        bigint taggable_id UK
        varchar taggable_type UK
    }
    tags {
        bigint id PK
        varchar name UK
    }
    telemetry_events {
        bigint id PK
        bigint user_id FK
    }
    users {
        varchar email UK
        bigint id PK
    }
Loading

Installation

Install as a dev dependency:

composer require --dev byrcsc/laravel-cartographer

Publish the config file if you want to change the defaults:

php artisan vendor:publish --tag=cartographer-config

How it works

Cartographer never reads your data. It builds the diagram from two sources:

  • Your models. Relationship methods are resolved through Eloquent itself, so foreign keys, pivot tables, and polymorphic names come out exactly as your application resolves them. This covers belongsTo, hasOne, hasMany, belongsToMany, every morph* relation, and the through relations.
  • Your schema. Tables, columns, types, and keys come from Laravel's native schema introspection on your development connection. Structure only: no query ever touches your rows.

Because the schema is read from the database, it stays correct on apps with squashed migrations, raw SQL migrations, and long migration histories.

Quick start

Generate the full ERD to docs/erd.md:

php artisan cartographer:erd

Everything is included by default. Use the options to limit it:

Option Default Purpose
--models=Order,Invoice all models Seed models to focus on
--depth=2 unlimited How many relationship hops to follow from the seeds
--columns=all|keys|none all Column detail per entity. keys shows only PK, FK, and unique columns
--exclude-relations=through,morph none Relation types to leave out
--format=markdown|mmd markdown Markdown with a mermaid fence, or a raw .mmd file
--output=docs/erd.md from config Where the file is written
--stdout off Print to stdout instead of writing a file
--connection= default connection Which database connection to introspect

Output is deterministic: tables, columns, and edges are always emitted in the same order, so the committed ERD only changes when your schema or models do.

Configuration

return [
    // Directories scanned for models. Globs are supported,
    // so modular layouts work: 'src/Domain/*/Models'.
    'paths' => [
        app_path('Models'),
    ],

    // Model classes to leave out of every diagram.
    'exclude_models' => [],

    // Connection to introspect. Null uses your default connection.
    'connection' => null,

    // Output location, relative to the project root.
    'output' => 'docs/erd.md',

    // Renderer format: markdown | mmd.
    'format' => 'markdown',

    'columns' => [
        // Column detail per entity: all | keys | none.
        'mode' => 'all',

        // Columns to hide, keyed by model class.
        'exclude' => [],
    ],

    'relations' => [
        // Relation types to leave out: 'through', 'morph', ...
        'exclude' => [],

        // Only inspect methods with a declared Relation return type.
        'strict_types_only' => false,
    ],
];

The published config/cartographer.php documents every key inline. Defaults and validation rules are in the configuration reference under Documentation below.

A note on how relations are found

Cartographer instantiates each model and invokes its public zero-argument methods inside a guard, keeping only the ones that return a Relation. Building a relation object does not run a query. If your models have zero-argument methods with side effects, either exclude those models or turn on strict_types_only and type your relation methods.

Documentation

Out of scope

Things this package will not do, so you can decide quickly whether it fits:

  • Parse migration files. Migrations are history, not a schema. The database structure is the single source of truth.
  • Read table data. No query ever runs against your rows. Introspection reads structure only.
  • Support Laravel 11 or older. Laravel 11 no longer receives security patches. Laravel 10 and older also need doctrine/dbal for introspection, which is a permanent second code path for end-of-life framework versions.
  • Support ORMs other than Eloquent.
  • Add routes, pages, or anything browser-facing to your app. Cartographer is an artisan command that writes files.

Rendering Mermaid to PNG or SVG is not in the package today. GitHub, GitLab, and mmdc already render the output; a built-in exporter may be considered later if there is real demand.

Versioning

The package follows semantic versioning.

  • Upgrading within 1.x is safe. Nothing you use will break.
  • Only a new major version, like 2.0.0, can break your code.
  • If the README or the documentation describes it, it is safe to build on. If they don't, treat it as internal and expect it to change.

Concretely, that covers the cartographer:erd command and its options, the config file keys and their defaults, and the classes on the PHP API reference page.

One deliberate exception: the exact layout of the generated diagram is not covered. Cardinality notation, edge labels, and column ordering may be refined in a minor release, which will show up as a diff in your committed ERD the next time you regenerate it. Determinism is guaranteed, so a given schema always produces the same file on a given version, but the rendering itself is free to improve. Anything that moves lines gets a changelog entry.

Bug fixes go into the newest version only. To get a fix, upgrade to it.

Questions and issues

  • Stuck, or have an idea? Start a discussion. Usage questions and feature ideas both live there.
  • Found a bug you can reproduce? Open an issue. A failing test is the fastest way to a fix, and a short reproduction is the next best thing.
  • Found a security problem? Please don't open a public issue. See SECURITY.md for how to report it privately.
  • Planning a pull request? CONTRIBUTING.md covers the setup and the three checks it needs to pass.

This package is maintained by one person, so replies can take a while. Everything gets read.

License

MIT. See LICENSE.md. Changelog in CHANGELOG.md.