›
byrcsc/laravel-cartographer · 1.x
Cartographer reads tables, columns, and keys without reading application data.
Cartographer asks Laravel's schema builder for every table on the selected connection. It reads columns, indexes, and foreign key constraints from the database.
The database must be reachable and migrated before you generate a diagram. In return, the diagram reflects the current structure even when the application uses squashed migrations, raw SQL, or columns added outside a migration.
Schema introspection means reading information about the database structure.
Cartographer reads information_schema on MySQL, system catalogs on PostgreSQL,
and PRAGMA data on SQLite. It never selects rows from an application table.
The connection therefore only needs permission to read schema metadata.
php artisan cartographer:erd --connection=reporting// config/cartographer.php
'connection' => 'reporting',The --connection option wins over the config key, and null in config means
the application's default connection. The value must be null or a non-empty
string; anything else fails the command.
Each run reads one connection. If your models use several connections, generate one diagram for each connection. See diagrams per subsystem.
When the connection cannot be opened or introspected, the command fails with:
ERROR Unable to introspect database connection [reporting]. Check the connection name, credentials, and database availability.The same message covers an unknown connection name, wrong credentials, and a
database that is not running. The original driver exception is kept as the
previous exception on SchemaReadException.
The same application on three drivers reports three sets of type names. Cartographer maps them onto one small vocabulary, so the committed diagram does not change when a developer runs it against SQLite and CI runs it against MySQL.
| Rendered as | Driver types collapsed into it |
|---|---|
bigint | bigint, bigserial, int8 |
integer | int, int4, integer, mediumint, serial |
smallint | int2, smallint, smallserial, tinyint |
varchar | varchar, char, character, character varying, bpchar, uuid |
timestamp | timestamp, timestamptz, timestamp with time zone, timestamp without time zone, datetime, datetime2, smalldatetime |
text | text, tinytext, mediumtext, longtext, json, jsonb |
decimal | decimal, numeric, float, real, double, double precision |
boolean | bool, boolean, and any tinyint(1) |
Three rules shape the table:
varchar(255) and varchar(64) both
render as varchar. The diagram is about shape, not storage.unsigned is dropped. bigint unsigned renders as bigint.tinyint(1) is boolean. MySQL's boolean is a tinyint(1), and it is
matched on the native type before anything else.A type the normalizer does not recognise passes through unchanged, with its
original spelling and whitespace. A PostGIS geometry column stays geometry;
a vector column stays whatever the driver called it. Nothing is lost, and future
column types need no package release.
int and integer render as bigint when the column takes part in a key, and
integer when it does not.
"Takes part in a key" means the column appears in any index, of any kind, or in
any foreign key constraint. SQLite reports id as INTEGER where MySQL and
PostgreSQL report bigint; without this rule, the same application would
produce a different diagram per driver on nearly every primary and foreign key.
The effect is confined to int-family types. A non-key integer column stays
integer:
attachments {
bigint id PK
integer size_bytes
}Each column line carries the markers that apply to it, in this order:
| Marker | Condition |
|---|---|
PK | The column appears in the table's primary key |
FK | The column appears in a foreign key constraint |
UK | The column appears in a unique index and is not part of the primary key |
profiles {
bigint id PK
bigint user_id FK, UK
}Composite keys mark every column they contain. PK suppresses UK on the same
column, because a primary key is unique by definition and the second marker adds
nothing.
Two things follow, and both surprise people at least once:
FK requires a real constraint. Applications that model relationships in
Eloquent but never declare foreign keys in the database get correct edges and no
FK markers. The edges come from the models; the markers come from the schema.
Plain indexes are invisible. A non-unique index affects nothing you can see, though it does count towards the integer-to-bigint rule above. Mermaid entity blocks have no notation for it.
Introspection reads every table on the connection, but the graph only uses two kinds:
Cartographer ignores a table that has no discovered model. This includes legacy
log tables, framework tables such as jobs and cache, and tables created by
other packages. The model list acts as the table filter.
Tables, columns, indexes, and foreign keys are each sorted by name before use. Column order in the diagram is alphabetical, not the physical column order of the table. Drivers do not agree on the order they return metadata in, and a diagram that reshuffles itself between machines is not reviewable.