Features

Collections

Collections define content schemas (EAV). ContentCollectionController manages collection metadata; FieldController drives the fields builder (reorder, duplicate, form visibility, layout width, form layout tabs/sections). Pages live under resources/js/pages/collections/collections/.

Authorization

routes/collections.php uses auth + verified plus Spatie permission: middleware on every action (can-*-collections; bulk authorize in FormRequest). The React UI (useCan) and AI tools share the same names. Item read/write also applies CollectionPermissionEnforcer (role collection_permissions field ACL / item_filter). Details: Effective permissions, Collections API. Data shape: Collections data model. Field types: Field types.

Controllers & pages

LayerLocation
Collection controllerApp\Http\Controllers\Collections\ContentCollectionController
Field controllerApp\Http\Controllers\Collections\FieldController
ModelApp\Models\Collection (Spatie Sortable, soft deletes)
Field modelApp\Models\CollectionField (sortable, scoped per collection)
Routesroutes/collections.php
Pagesresources/js/pages/collections/collections/index.tsx, show.tsx, fields.tsx

Collection CRUD

ActionRouteInertia / behavior
IndexGET /collectionscollections/collections/index; toolbar search (name/slug), sort (name/slug/updated_at), direction (asc/desc); ?trashed=1onlyTrashed(); drawer deep-links ?edit={id} / ?new=1
StorePOST /collectionsCreates collection; if is_singleton, seeds an empty item
ShowGET /collections/{collection}Non-singleton → redirect to items index; singleton → editor (show)
UpdatePUT /collections/{collection}Metadata via UpdateContentCollectionRequest
BulkPOST /collections/bulkaction: delete | restore | force_delete + ids
Create from packPOST /collections/packs/{pack}Collection packs (seo, articles, …) — see Field types
Soft-deleteDELETE /collections/{collection}Soft-delete
RestorePOST /collections/{collection}/restoreRestore
Force-deleteDELETE /collections/{collection}/forcePermanent delete

Slug

  • On create (StoreContentCollectionRequest): slug optional; empty → Str::slug(name); uniqueness via UniqueCollectionSlugGenerator.
  • On update: slug regenerated uniquely from name/slug as needed.

is_singleton

  • Boolean fillable on Collection.
  • Store coerces with $request->boolean('is_singleton').
  • Cannot change after create (422 on update attempts).
  • Singleton create seeds $collection->items()->create([]).
  • Singleton show uses assembled singletonRawData + relatedCollections. Content upsert is documented under Collection items.

Sortable

Collection implements Spatie’s sortable trait:

  • Order column: sort_order
  • sort_when_creating = true
  • Listing uses Collection::ordered()

Soft-delete cascade to items

Collection::booted():

Collection eventItems
Soft-delete$collection->items()->delete()
Force-delete$collection->items()->withTrashed()->forceDelete()
Restore$collection->items()->onlyTrashed()->restore()

Fields builder

Route prefix under each collection:

ActionRoute nameBehavior
Builder UIGET …/fieldscollections.fields.indexInertia collections/collections/fields; deep-links ?field={id} / ?newField=1
Create fieldPOST …/fieldsIncludes translatable boolean
Update fieldPATCH …/fields/{field}Definition update
Delete fieldDELETE …/fields/{field}Hard delete
ReorderPOST …/fields/reorderCollectionField::setNewOrder($ids); persists settings.layout_starts_new_row from starts_new_row_ids
DuplicatePOST …/fields/{field}/duplicatereplicate without sort_order; name {base}_copy / _copy_N
Toggle form visibilityPOST …/fields/{field}/toggle-form-visibilityToggles settings.hidden_in_form
Layout widthPOST …/fields/{field}/layout-widthSets settings.layout_width
Form layoutPUT …/form-layoutTabs/sections JSON
Field packPOST …/field-packs/{pack}Apply pack (skip existing names) — Field packs

UI: Add field pack… on the fields page; Create from pack… on the collections index.

Collection permissions (enum)

can-show-collections
can-create-collections
can-edit-collections
can-delete-collections
can-restore-collections
can-force-delete-collections

Sidebar show is gated by can-show-collections. Index trash actions use delete/restore/force-delete checks. Field and item pages currently do not wrap every control in useCan.

Source map

ConcernLocation
Collection controllerapp/Http/Controllers/Collections/ContentCollectionController.php
Field controllerapp/Http/Controllers/Collections/FieldController.php
Routesroutes/collections.php
Pagesresources/js/pages/collections/collections/*
Previous
Roles & permissions