Schema JSON
Besides the typed classes, the build can write the models out as plain JSON — a description of every table for something outside the project to read.
Turning it on
It is off unless you name a file for it, through
DB_SCHEMA_FILE in the .env:
DB_SCHEMA_FILE = "database"
SchemaJSON then writes
database.json at the root of the app on the next build, adding the
.json for you. It is a builder like any other, so
destroy removes the file again, and leaving the variable empty skips it entirely.
What it returns
One entry per table, sorted by name, covering the framework's own models as well as yours. Each entry repeats the flags from the #[Model] attribute and then lists the columns and how they are wired together:
{
"settings": {
"hasTimestamps": true,
"hasStatus": false,
"hasUsers": false,
"canCreate": false,
"canEdit": true,
"canDelete": false,
"fields": [
{ "name": "section", "type": "string", "length": 0, "isPrimary": true, "isKey": false },
{ "name": "variable", "type": "string", "length": 0, "isPrimary": true, "isKey": false },
{ "name": "value", "type": "text", "length": 0, "isPrimary": false, "isKey": false },
{ "name": "variableType", "type": "enum", "length": 0, "isPrimary": false, "isKey": false }
],
"joins": [],
"foreigns": []
}
}
| Key | Is |
|---|---|
fields | Every column of the table itself, as name, type, length, isPrimary and isKey. The type is the field type — number, string, text, longtext, boolean, date, enum or json. |
joins | The tables this one is read together with, from its relations. |
foreigns | The columns that point at another table without being joined into the entity. |
Both are given the same way — fromField, toTable, toField — so a
reader can treat them as edges without caring which produced them:
"joins": [
{ "fromField": "SESSION_ID", "toTable": "log_session", "toField": "SESSION_ID" },
{ "fromField": "CREDENTIAL_ID", "toTable": "credential", "toField": "CREDENTIAL_ID" }
]
Using it
The file is regenerated on every build, so it should be committed only if the thing reading it expects to find it in the repository — otherwise treat it like the rest of the generated code and ignore it. Nothing in the framework reads it back; it exists purely to be consumed from outside:
$schema = JSON::readFile($path, "database.json");
foreach ($schema as $tableName => $table) {
foreach ($table["joins"] as $join) {
// ... draw an edge from $tableName to $join["toTable"]
}
}
Because it is written from the same models the migrations read, it never drifts from the database the app actually builds.