Installation:
composer require plank/laravel-mediable
Publish the config and migrations:
php artisan vendor:publish --provider="Plank\Mediable\MediableServiceProvider"
php artisan migrate
First Use Case:
Upload a file and attach it to a model (e.g., Post):
use Plank\Mediable\HasMediable;
use Plank\Mediable\MediaUploader;
class Post extends Model
{
use HasMediable;
}
// In your controller:
$post = Post::find(1);
$media = MediaUploader::fromRequest($request, 'image')
->toDestination('public', 'posts/thumbnails')
->upload();
$post->addMedia($media)->usingCollection('thumbnails')->save();
Key Files:
config/mediable.php: Configure disks, allowed MIME types, and default settings.app/Models/Media.php: Base model for media records (extend if needed).app/Models/Mediable.php: Trait for polymorphic media relationships.$media = MediaUploader::fromRequest($request, 'image')
->toDestination('public', 'uploads')
->upload();
->validate(function ($file) {
return $file->isValidImage() && $file->size() < 5120; // <5MB
});
$request->file('images')->each(function ($file) use ($post) {
$media = MediaUploader::fromSource($file)
->toDestination('public', 'gallery')
->upload();
$post->addMedia($media)->usingCollection('gallery')->save();
});
$post->addMedia($media); // Attaches to default collection
$post->addMedia($media)->usingCollection('featured')->save();
$media->tags()->sync(['thumbnail', 'primary']);
$post->getMedia('thumbnails'); // Collection of media
$post->getFirstMedia('featured'); // Single media
$post->getMedia('thumbnail'); // Media tagged 'thumbnail'
$media->getUrl(); // Full URL
$media->getPath(); // Filesystem path
intervention/image)$media->setCustomProperty('width', 300)->setCustomProperty('height', 200);
$media->generateVariant();
$media->getVariantUrl('thumbnail'); // URL to generated variant
$post->deleteMedia($media); // Removes from all collections
$post->deleteMedia($media, 'thumbnails'); // Removes from specific collection
$post->clearMediaCollections();
Extend the Media model to add custom fields:
class CustomMedia extends \Plank\Mediable\Models\Media
{
protected $casts = [
'is_primary' => 'boolean',
];
}
Update config/mediable.php to point to your custom model.
Override the upload method in a service class:
class CustomMediaUploader extends MediaUploader
{
public function upload()
{
// Custom logic (e.g., pre-process files)
return parent::upload();
}
}
Configure multiple disks in config/mediable.php:
'disks' => [
'public' => 'public',
's3' => 's3',
],
Use them in uploads:
->toDestination('s3', 'backups')
Serialize media with relationships:
return PostResource::make($post)->additional([
'media' => $post->getMedia()->map(fn ($m) => [
'url' => $m->getUrl(),
'tags' => $m->tags,
]),
]);
Use MediaUploader in tests:
$media = MediaUploader::fromSource(fake()->image())
->toDestination('public', 'tests')
->upload();
$this->assertDatabaseHas('media', ['path' => 'tests/...']);
Filesystem Permissions:
public) has write permissions.s3, verify AWS credentials and bucket policies.MIME Type Restrictions:
config/mediable.php:
'allowed_mime_types' => [
'image/jpeg', 'image/png', 'image/gif',
'application/pdf', 'application/zip',
],
Polymorphic Conflicts:
HasMediable, ensure the mediable_type column in the media table doesn’t cause collisions (unlikely, but possible with custom table names).Variant Generation:
intervention/image must be installed (composer require intervention/image).$media->generateVariant(['width' => 300, 'height' => 300], 'thumbnail');
Large Files:
max_file_size in php.ini may block uploads. Adjust as needed.spatie/laravel-medialibrary or custom solutions.Tagging Quirks:
const TAG_THUMBNAIL = 'thumbnail';
Model Events:
deleting events on the model. Override if needed:
protected static function bootHasMediable()
{
static::deleting(function ($model) {
$model->clearMediaCollections();
});
}
Log Uploads:
Add a logging middleware to MediaUploader:
->beforeUpload(function ($file) {
\Log::debug('Uploading', ['file' => $file->getClientOriginalName()]);
});
Check Disk Contents: Manually verify files exist in the configured disk:
ls storage/app/public/uploads
Validate MIME Types:
Use dd($request->file('image')->getMimeType()) to debug rejected uploads.
Query Media:
Inspect the media table directly:
\DB::table('media')->where('model_id', $post->id)->get();
Clear Cached Variants:
Delete storage/framework/cache/ if variants aren’t regenerating.
Custom Storage Engines:
Extend Plank\Mediable\Contracts\MediaUploader to support non-filesystem storage (e.g., database blobs).
Hooks:
Override methods in MediaUploader:
beforeUpload: Pre-process files.afterUpload: Post-upload actions (e.g., send notifications).beforeDelete: Cleanup logic.Custom Collections:
Add logic to HasMediable trait:
public function addMedia($media, $collection = 'default')
{
if ($collection === 'custom') {
// Custom logic
}
return parent::addMedia($media, $collection);
}
API Resources:
Create a MediaResource for consistent API responses:
class MediaResource extends JsonResource
{
public function toArray($request)
{
return [
'url' => $this->getUrl(),
'tags' => $this->tags,
'size
How can I help you explore Laravel packages today?