Tagixo Docs

Developer Documentation for Laravel, SDK integrations, and extensibility

Resources

Media Gallery

Upload, storage, thumbnails, auto-cropping, and the REST + Filament UI surfaces.

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 URL
  • filename / original_filename — stored vs. user-uploaded name
  • disk / path — Laravel filesystem reference
  • mime_type, extension, size, width, height
  • alt_text, title, description — editable metadata
  • folder — optional grouping path
  • metadata (JSON) — extension point for app-specific fields
  • uploaded_by — FK to auth.providers.users.model
  • parent_id — null on originals, set on crops

Relations:

  • uploader(): BelongsTo → User
  • original(): 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:

  1. A thumbnail at {dir}/thumbnails/{filename}_thumb.{ext} — 300×300 cover crop, quality 85
  2. A set of auto-crops based on the tagixo.media_gallery.crop_presets config

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)
  • MediaGallery admin 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.