Installation
composer require ekyna/media-bundle
Add to config/bundles.php:
Ekyna\MediaBundle\EkynaMediaBundle::class => ['all' => true],
Database Migration Run:
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate
The bundle creates tables for media (files), folders, and media_gallery (if enabled).
First Use Case: Upload a File
Ekyna\MediaBundle\Form\Type\MediaType:
use Ekyna\MediaBundle\Form\Type\MediaType;
$builder->add('media', MediaType::class, [
'label' => 'Upload Media',
'mapped' => false, // For standalone uploads
]);
Media entity persistence.Basic Configuration
Edit config/packages/ekyna_media.yaml (auto-generated):
ekyna_media:
upload_dir: '%kernel.project_dir%/public/uploads'
allowed_mime_types: ['image/jpeg', 'image/png', 'application/pdf']
max_file_size: 10M
File Upload & Storage
Media entity for file metadata (e.g., title, description, folder).public function upload(Request $request, MediaManager $mediaManager)
{
$media = $mediaManager->upload($request->file('media'));
return new JsonResponse(['url' => $media->getUrl()]);
}
save() to persist to DB:
$mediaManager->upload($file)->save();
Folder Management
Folder entity or FolderManager:
$folder = $folderManager->create('images', 'root');
$media->setFolder($folder);
tree structure for nested folders (e.g., root/photos/vacation).Gallery Integration
gallery feature in config:
ekyna_media:
gallery:
enabled: true
Gallery entity or GalleryManager:
$gallery = $galleryManager->create('My Gallery');
$gallery->addMedia($media);
Frontend Integration
MediaType with Turbo for live previews.{{ include('EkynaMediaBundle::upload.html.twig', {
'mediaType': mediaType,
'options': {'maxFileSize': 1000000}
}) }}
data-validate-* attributes (e.g., data-validate-max-number="3").API Endpoints
MediaController (auto-registered) for CRUD:
GET /api/media → List media.POST /api/media → Upload.config/routes.yaml:
ekyna_media:
resource: '@EkynaMediaBundle/Resources/config/routing.yaml'
prefix: '/api'
Event Listeners
MediaEvents (e.g., MediaUploadEvent):
public static function getSubscribedEvents()
{
return [
MediaEvents::MEDIA_UPLOADED => 'onMediaUploaded',
];
}
Custom Fields
Media entity with traits or inheritance:
class CustomMedia extends Media
{
private $customField;
// Add getter/setter + DB mapping.
}
Validation
MediaValidator for custom rules:
$validator->addConstraint(new CustomMediaConstraint());
Symfony Messenger
MediaUploadedMessage for async processing:
$bus->dispatch(new MediaUploadedMessage($media));
Missing upload_dir Permissions
Permission denied on upload.public/uploads is writable:
chmod -R 775 %kernel.project_dir%/public/uploads
MIME Type Restrictions
allowed_mime_types blocks common formats.ekyna_media:
allowed_mime_types: ['image/*', 'video/*', 'application/pdf']
Gallery Positioning
GalleryMediaSubscriber may not set position correctly.position in GalleryMedia:
$galleryMedia->setPosition($media->getId()); // Or custom logic.
JS Upload Validation
data-validate-* attributes in Blueimp config:
$.ajaxSetup({
beforeSend: function() {
console.log('Validation rules:', $('#fileupload').data('validate'));
}
});
Folder Persistence
$folderManager->save($folder);
Upload Failures
MediaEvents::MEDIA_UPLOAD_FAILED event payload for errors.ekyna_media:
debug: true
Entity Mapping
make:entity to regenerate Media if custom fields break migrations.Route Conflicts
config/routes.yaml to avoid clashes with other bundles.Custom Storage
Ekyna\MediaBundle\Storage\StorageInterface for S3/Azure:
class S3Storage implements StorageInterface {
public function save(File $file): string { ... }
}
ekyna_media:
storage: Ekyna\MediaBundle\Storage\S3Storage
Media Transformers
MediaTransformer for custom image/video processing:
$transformer = new CustomTransformer();
$media->setThumbnail($transformer->transform($media));
Event Subscribers
GalleryMediaSubscriber for custom gallery logic (e.g., auto-tagging).Twig Extensions
EkynaMediaBundle:
$twig->addFunction(new \Twig\TwigFunction('media_url', [$mediaManager, 'getUrl']));
How can I help you explore Laravel packages today?