Media Gallery
Tagixo ships a complete media gallery: upload, storage abstraction, thumbnail generation, auto-cropping, and a browsable admin UI. The data layer lives in the core (ccast/tagixo); the admin UI components currently ship with the Filament SDK (ccast/tagixo-filament).
The Media model
Stored in the media_items table. Originals and crops live in the same table — crops carry a parent_id pointing at the original.
Key columns:
uuid— primary identifier shared with the public URLfilename/original_filename— stored vs. user-uploaded namedisk/path— Laravel filesystem referencemime_type,extension,size,width,heightalt_text,title,description— editable metadatafolder— optional grouping pathmetadata(JSON) — extension point for app-specific fieldsuploaded_by— FK toauth.providers.users.modelparent_id— null on originals, set on crops
Relations:
uploader(): BelongsTo→ Useroriginal(): BelongsTo→ Media (parent)crops(): HasMany→ Media (children)
Scopes you'll use often: originals(), images(), videos(), documents(), inFolder(?string), search(string).
Boot hooks: a UUID is auto-generated on creating; on deleting the model removes the physical file plus its thumbnail.
Storage abstraction
Tagixo does not pick a disk — it honours whatever you configure in config('filesystems.disks') and passes through Laravel's Storage facade.
URL generation adapts automatically:
- Public disk →
Storage::url($media->path)— direct, permanent URL - Private / S3 / MinIO →
Storage::temporaryUrl($media->path, now()->addDay())— signed 24h URL
If your storage moves between environments (local → S3 in production), nothing in the builder JSON needs to change because the renderer always asks the model for the current URL.
REST API
All media endpoints live under /tagixo/media/*:
| Method | URI | Purpose |
|---|---|---|
| GET | /tagixo/media |
List with pagination, search, type and folder filters |
| GET | /tagixo/media/folders |
List folders with item counts |
| POST | /tagixo/media/upload |
Multipart upload (files[] + optional folder) |
| PUT | /tagixo/media/{id} |
Update metadata |
| DELETE | /tagixo/media/{id} |
Delete |
| GET | /tagixo/media/{id}/variants |
List original + crops |
Upload example
curl -X POST "https://app.example.com/tagixo/media/upload" \
-H "X-CSRF-TOKEN: ..." \
-F "files[]=@/path/to/image.jpg" \
-F "folder=marketing/hero"
Response: {"uploaded": [MediaItem, ...], "errors": [...]}.
A MediaItem is a JSON representation including id, uuid, url, thumbnail_url, mime_type, dimensions, formatted size, and the user-editable metadata fields.
List example
GET /tagixo/media?search=hero&type=image&folder=marketing&page=1&perPage=24
Thumbnails and auto-crops
On every non-SVG image upload the package generates:
- A thumbnail at
{dir}/thumbnails/{filename}_thumb.{ext}— 300×300 cover crop, quality 85 - A set of auto-crops based on the
tagixo.media_gallery.crop_presetsconfig
Default presets:
'crop_presets' => [
'thumbnail' => ['mode' => 'crop', 'width' => 150, 'height' => 150],
'4x3' => ['mode' => 'contain', 'width' => 800, 'height' => 600],
'16x9' => ['mode' => 'contain', 'width' => 1920, 'height' => 1080],
],
Each preset produces a child Media record linked to the original. Auto-crop generation runs asynchronously through the GenerateMediaCrops queued job ($tries = 3, $timeout = 120s).
If you want manual cropping after upload, use the variants endpoint to list existing crops and call the crop endpoint with {x, y, width, height} pixel coordinates.
Permissions
Operations are gated by the manage-media permission, checked via $user->isAbleTo('manage-media') (the package uses Laratrust by default but the policy contract is generic). The published MediaGalleryPermissionSeeder creates the permission for you.
For multi-role setups, wire the permission to your role manager. The policy is enforced at the controller level — direct model writes from console commands or background jobs are exempt.
Filament SDK components
When you enable withMediaGallery() on the Filament plugin, four UI surfaces become available:
use Ccast\TagixoFilament\TagixoFilamentPlugin;
$panel->plugin(
TagixoFilamentPlugin::make()->withMediaGallery()
);
MediaSelector— modal browser (Browse / Upload tabs, search, folder filter, type filter, grid/list views, single or multiple selection)MediaGalleryField— a Filament form field for picking media (config:multiple,maxFiles,acceptedFileTypes,maxSize,enableCrop)MediaColumn— a Filament table column for displaying media thumbnails (config:lightbox,showTitle,size)MediaGalleryadmin page — full standalone page with statistics, table, upload action, edit/delete actions
These integrate directly with Filament's reactive system: selecting a media item dispatches a browser CustomEvent that any Alpine-based field listens for.
Primix SDK
The Primix SDK exposes the same data model (Media + the REST API). UI integration follows the LiVue conventions — see the Primix integration page for plugin wiring.
Backup and migration tips
The media_items table is the source of truth — backing it up alongside your storage disk is enough to fully restore the gallery. If you migrate storage providers, write a one-off script that streams each Media->path from old to new disk and updates disk/path columns. URLs regenerate automatically on next page render.