Database Schema

35 tables, all UUID-keyed, with JSON columns where flexible schema is needed. Migrations live in database/migrations/.

Conventions

Tables by domain

Identity & access

TablePurpose
usersAdmin users. Email, hashed password, avatar, last login.
rolesNamed roles with a JSON permissions map.
user_rolesPivot — many-to-many between users and roles.
password_reset_tokensStandard Laravel reset tokens.
sessionsLaravel session table (DB-backed sessions).

Content engine

TablePurpose
content_modelsDefinition of a content type (collection or single). Holds slug, naming, flags (is_localized, is_single, has_seo), JSON settings, computed_fields, and api_config.
content_fieldsField definitions for each model: type, validation, default, ordering.
content_entriesRecords belonging to a content model. Stores per-entry values in a JSON data column with locale, status, slug, seo, scheduled/published timestamps.
content_revisionsImmutable snapshot of an entry on each save; supports diff and restore.
content_relationsCross-entry relationships (one-to-many, many-to-many, etc.).
componentsReusable field groups that can be embedded into models or dynamic zones.

Media

TablePurpose
media_foldersHierarchical folder tree (self-referential).
media_filesUploaded file metadata: path, mime, size, dimensions, alt text.

Forms

TablePurpose
formsForm definitions with field schema, notification settings, slug for public endpoint.
form_submissionsPer-submission payload, status (new/read/spam), IP, user agent, internal notes.

API & integrations

TablePurpose
api_keysPublic key + hashed secret, JSON scopes, JSON allowed_origins, rate limit, expiry, usage counters.
api_request_logsPer-request audit row: API key, endpoint, status, duration, IP.
webhooksOutbound webhook subscriptions: URL, events, secret, retry policy.
webhook_deliveriesDelivery attempts with payload, response code/body, retry count.

Localization & site config

TablePurpose
languagesSupported locales. One default, others toggleable; supports is_translatable on settings.
settingsKey/value site settings, scoped by group, with optional translations.

Observability & messaging

TablePurpose
activity_logsPolymorphic audit log: actor, action, subject, diff, IP, user agent.
notificationsLaravel notifications.
jobsQueue jobs.
job_batchesBatched job metadata.
failed_jobsFailed queue jobs.
cacheCache store (DB driver).
cache_locksCache lock keys.

Key relationships

content_models 1 ── ∞ content_fields       (model defines fields)
content_models 1 ── ∞ content_entries      (entries belong to a model)
content_entries 1 ── ∞ content_revisions   (every save → 1 revision)
content_entries ∞ ── ∞ content_entries     (via content_relations)
users 1 ── ∞ content_entries               (created_by / updated_by)
users ∞ ── ∞ roles                         (via user_roles)
media_folders 1 ── ∞ media_files
media_folders 1 ── ∞ media_folders         (self-referential tree)
forms 1 ── ∞ form_submissions
api_keys 1 ── ∞ api_request_logs
webhooks 1 ── ∞ webhook_deliveries

Example: content_entries

id                  char(36)   PK
content_model_id    char(36)   FK → content_models.id (cascade)
title               varchar(500) nullable
slug                varchar(500) nullable
status              varchar(20)  default 'draft'        — draft|published|scheduled|archived
locale              varchar(10)  default 'en'
data                json                              — field values keyed by field slug
seo                 json         nullable
computed_data       json         nullable              — server-evaluated computed fields
published_at        timestamp    nullable
scheduled_at        timestamp    nullable
created_by          char(36)     FK → users.id (null on delete)
updated_by          char(36)     FK → users.id (null on delete)
created_at, updated_at, deleted_at

UNIQUE (content_model_id, slug, locale)
INDEX  (content_model_id, status, updated_at)
INDEX  (content_model_id, locale, status)
INDEX  (status, scheduled_at)
INDEX  on CAST(data->>'$.title' AS CHAR(255))    — MySQL 8 functional index

Example: api_keys

id                char(36)   PK
name              varchar(255)
key               varchar(64)  UNIQUE       — public identifier (sent in header)
secret_hash       varchar(255)              — bcrypt-hashed secret
scopes            json                      — ["read", "write", "delete"]
allowed_origins   json         nullable     — CORS allowlist
rate_limit        int unsigned default 60   — requests per minute
usage_count       bigint unsigned default 0
last_used_at      timestamp    nullable
last_used_ip      varchar(45)  nullable
expires_at        timestamp    nullable
is_active         bool         default true
created_by        char(36)     FK → users.id (null on delete)
Migrations live with the codeSee database/migrations/ for the authoritative schema. The seeded dm_editor.sql file at the project root is a one-shot SQL dump if you want to bootstrap without running Laravel.