Entries & Revisions
Entries are the actual content records inside a model. Every save creates an immutable revision so editors can audit, diff, and restore.
Entry routes
Entries are scoped under their content model's slug.
| Route | Method | Purpose |
|---|---|---|
/admin/content/{model:slug}/entries | GET | List entries (search, filter, sort, paginate) |
/admin/content/{model:slug}/entries/create | GET | New entry form |
/admin/content/{model:slug}/entries | POST | Create entry |
/admin/content/{model:slug}/entries/{entry} | GET | Edit entry |
/admin/content/{model:slug}/entries/{entry} | PUT | Update entry |
/admin/content/{model:slug}/entries/{entry} | DELETE | Soft delete |
/admin/content/{model:slug}/single | GET | Shortcut for single-type models |
Entry status
| Status | Meaning |
|---|---|
draft | Hidden from the public API. |
published | Visible to the public API. |
scheduled | Auto-publishes at scheduled_at via a scheduler job. |
archived | Hidden from listings but kept for reference. |
Field data shape
Field values are stored in the data JSON column, keyed by field slug:
{
"title": "Hello world",
"slug": "hello-world",
"excerpt": "First post.",
"body": "<p>Long form rich text…</p>",
"tags": ["intro", "release"],
"hero_image": "f3c2…media-uuid…",
"related_posts": ["uuid-1", "uuid-2"]
}
Localization
When the model has is_localized = true, entries are scoped per locale. The unique constraint on (content_model_id, slug, locale) allows the same slug to exist across languages. Editors switch locale from the entry list header.
SEO subform
If has_seo is enabled on the model, the entry form renders an SEO panel writing to the seo JSON column:
{
"title": "Meta title",
"description": "Meta description, ~160 chars",
"og_image": "media-uuid",
"no_index": false,
"canonical": "https://example.com/posts/hello"
}
Revisions
Every store and update creates a content_revisions row containing a complete snapshot of the entry's data, seo, and metadata at that point in time.
| Route | Method | Purpose |
|---|---|---|
…/entries/{entry}/revisions | GET | List revisions (newest first, with author + diff summary) |
…/entries/{entry}/revisions/{revision} | GET | Show a single revision (full snapshot) |
…/entries/{entry}/revisions/{revision}/restore | POST | Restore — applies the snapshot and creates a new revision representing the restore |
Activity logging
An observer writes a row to activity_logs on every create / update / delete, capturing the actor, IP, and a JSON diff of changed fields.
Validation
Per-field validation is computed dynamically from the model's content_fields + validation JSON. Required fields, unique fields, numeric ranges, and pattern matches are all rolled into Laravel rule strings on form submission.