aboleon / metaframework-mediaclass
Mediaclass media management components for MetaFramework
Installs: 2
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/aboleon/metaframework-mediaclass
Requires
- php: ^8.3
- aboleon/metaframework-inputable: ^0.1
- aboleon/metaframework-support: ^0.1
- illuminate/database: ^11.0|^12.0
- illuminate/filesystem: ^11.0|^12.0
- illuminate/http: ^11.0|^12.0
- illuminate/routing: ^11.0|^12.0
- illuminate/support: ^11.0|^12.0
- illuminate/view: ^11.0|^12.0
README
Media management components for MetaFramework (Laravel). This package provides upload UI, database persistence, image resizing, optional cropping, and helpers to retrieve and render media for Eloquent models.
What this package does
- Adds a polymorphic
mediaclasstable to store images and files for any model. - Provides Blade components for uploads, listings, and rendering images.
- Generates resized image variants and optional crops per group.
- Supports per-group settings (dimensions, labels, crop variants).
- Handles "ghost" media (records without a model_id) for pre-save workflows.
- Provides Parser and Printer helpers to fetch and render URLs cleanly.
Requirements
- PHP 8.3+
- Laravel 11 or 12
- Intervention Image (used for resizing/cropping)
- jQuery + Blueimp jQuery File Upload (bundled in assets)
- Bootstrap modals and icon fonts (used by the UI templates)
Installation
Add the package (path repo already configured in composer.json):
composer require aboleon/metaframework-mediaclass
Publish assets, config, language files, and migrations:
php artisan vendor:publish --tag=mfw-mediaclass-config php artisan vendor:publish --tag=mfw-mediaclass-views php artisan vendor:publish --tag=mfw-mediaclass-lang php artisan vendor:publish --tag=mfw-mediaclass-assets php artisan vendor:publish --tag=mfw-mediaclass-migrations
Run the migration:
php artisan migrate
Configuration
The config file is published to config/mfw-mediaclass.php.
return [ 'disk' => 'public', 'dimensions' => [ 'xl' => ['width' => 1920, 'height' => 1080], 'lg' => ['width' => 1400, 'height' => 788], 'md' => ['width' => 700, 'height' => 394], 'sm' => ['width' => 400, 'height' => 225], ], ];
Note: media URLs are built using the media disk in MetaFramework\Mediaclass\Models\Media.
Make sure filesystems.disks.media exists (often same config as public), or align
your storage setup accordingly.
Model setup
Implement the interface and use the trait on your Eloquent model:
use Illuminate\Database\Eloquent\Model; use MetaFramework\Mediaclass\Interfaces\MediaclassInterface; use MetaFramework\Mediaclass\Traits\Mediaclass as MediaclassTrait; class Post extends Model implements MediaclassInterface { use MediaclassTrait; public function mediaclassSettings(): array { return [ 'cover' => [ 'label' => 'Cover', 'width' => 1600, 'height' => 900, 'cropable' => true, ], 'gallery' => [ 'label' => 'Gallery', 'width' => 1200, 'height' => 800, 'cropable' => [ 'thumb' => [400, 300, 'Thumb'], 'banner' => [1600, 400, 'Banner'], ], ], ]; } }
Call processMedia() after create/update to persist descriptions and positions
and to attach temporary uploads:
$post = Post::create($payload); $post->processMedia();
Blade components
Uploadable component
<x-mediaclass::uploadable :model="$post" group="cover" :limit="1" :positions="true" />
With subgroup + custom storables:
<x-mediaclass::uploadable :model="$post" group="gallery" :settings="['subgroup' => 'front']" :storables="['slot' => 'hero']" />
Make sure your layout includes the stacks used by the component:
@stack('css') @stack('js')
Stored component
<x-mediaclass::stored :model="$post" group="gallery" />
Printer component
@php $cover = Mediaclass::forModel($post, 'cover')->first(); @endphp <x-mediaclass::printer :model="$cover" size="lg" class="rounded" /> <x-mediaclass::printer :model="$cover" type="picture" />
Backend usage
Fetch and parse media
use MetaFramework\Mediaclass\Facades\Mediaclass; $cover = Mediaclass::forModel($post, 'cover')->first(); if ($cover) { $url = $cover->url; // default URL (cropped preferred) $thumb = $cover->getUrl('sm'); }
Render with Printer
use MetaFramework\Mediaclass\Printer; $cover = Mediaclass::forModel($post, 'cover')->first(); $imgTag = $cover ? (new Printer($cover))->setClass('rounded')->img('md') : '';
Helper
{{ mediaclass_url($cover, 'md') }}
Ghost media URL
use MetaFramework\Mediaclass\Mediaclass; $url = Mediaclass::ghostUrl(Post::class, 'cover', 'sm', '/img/fallback.png');
Cropping
Cropping is driven by the cropable setting:
true: a single crop based on the group dimensions.[w, h]: a single crop with given dimensions.['key' => [w, h, 'Label']]: multiple crops, keyed and labeled.
Crop variants are stored as files named:
cropped_<key>_<filename>.<ext>
The Parser and Printer classes automatically prefer cropped images when available.
Ghost uploads vs temp uploads
- Temp uploads: when a model has no id yet, the component injects a
mediaclass_temp_idfield;processMedia()attaches those records after save. - Ghost uploads: set
ghost="true"on the component to store media withmodel_id = null. UseMediaclass::ghostUrl()to retrieve their URL.
Example:
<x-mediaclass::uploadable :model="$post" group="cover" ghost />
File types and limits
Accepted types: JPEG, PNG, SVG, PDF. Size is controlled by the maxfilesize
attribute on the upload component (default is 16MB).
Storage layout
- Regular models:
snake(model)/{id}/<width>_<filename>.<ext> - Ghost models:
snake(model)/<width>_<filename>.<ext> - Non-images:
snake(model)/{id}/<filename>.<ext>
Routes
The package registers:
POST /mediaclass-ajax(upload/delete/crop actions,webmiddleware)GET /mediaclass/cropable/{media}(crop UI,web+auth:sanctum)
Troubleshooting
- If thumbnails do not resolve, confirm the
mediadisk is configured. - If crops do not show, ensure the crop route is reachable and assets are published.
- If the UI is missing, verify
@stack('css')and@stack('js')are in your layout.