coka/attachment-manager-bundle
Installation Add the bundle via Composer:
composer require coka/attachment-manager-bundle
Enable it in config/bundles.php:
return [
// ...
CedrickOka\AttachmentManagerBundle\OkaAttachmentManagerBundle::class => ['all' => true],
];
Configuration Publish the default config:
php bin/console oka:attachment-manager:install
Update config/packages/oka_attachment_manager.yaml to match your storage (e.g., local, s3, gcs).
First Use Case Upload an attachment via a form:
{{ form_start(form) }}
{{ form_widget(form.file) }} <!-- 'file' must be the field name -->
{{ form_end(form) }}
Handle the upload in your controller:
use CedrickOka\AttachmentManagerBundle\Service\AttachmentManager;
public function upload(Request $request, AttachmentManager $manager)
{
$file = $request->file('file');
$attachment = $manager->upload($file, 'user_attachments'); // 'user_attachments' = storage directory
return new JsonResponse(['url' => $attachment->getUrl()]);
}
Uploading Files
AttachmentManager service to handle uploads:
$attachment = $manager->upload($file, 'directory', [
'mime_type' => 'image/jpeg',
'custom_metadata' => ['user_id' => 123],
]);
oka_attachment_manager.yaml).Retrieving Attachments
$attachment = $manager->find('user_attachments', $attachmentId);
$attachments = $manager->list('user_attachments');
Deleting Files
$manager->delete('user_attachments', $attachmentId);
$manager->deleteMultiple('user_attachments', [$id1, $id2]);
Integration with Entities
/**
* @ORM\Column(type="string", nullable=true)
*/
private $attachmentId;
use Doctrine\ORM\Event\LifecycleEventArgs;
public function preRemove(LifecycleEventArgs $args)
{
$attachmentId = $this->attachmentId;
if ($attachmentId) {
$manager->delete('user_attachments', $attachmentId);
}
}
Symfony Forms
OkaAttachmentType for file uploads:
use CedrickOka\AttachmentManagerBundle\Form\Type\OkaAttachmentType;
$builder->add('avatar', OkaAttachmentType::class, [
'directory' => 'user_avatars',
'allowed_mime_types' => ['image/jpeg', 'image/png'],
]);
API Responses
$serializer = $this->container->get('serializer');
$attachmentData = $serializer->serialize(
$attachment,
'json',
['groups' => ['attachment:read']]
);
Custom Storage Adapters
Extend CedrickOka\AttachmentManagerBundle\Storage\StorageInterface for cloud providers (e.g., Backblaze B2):
class BackblazeStorage implements StorageInterface
{
public function save($file, $directory, array $options): Attachment
{
// Custom logic
}
}
Register in services.yaml:
services:
CedrickOka\AttachmentManagerBundle\Storage\StorageInterface:
class: App\Storage\BackblazeStorage
Event Listeners Trigger actions on upload/delete:
use CedrickOka\AttachmentManagerBundle\Event\AttachmentEvent;
$dispatcher->addListener(
'oka_attachment_manager.upload',
function (AttachmentEvent $event) {
// Log, notify, or process metadata
}
);
Validation Rules Enforce file size/mime types via constraints:
use CedrickOka\AttachmentManagerBundle\Validator\Constraints as OkaAssert;
/**
* @OkaAssert\File(
* maxSize="10M",
* mimeTypes={"image/jpeg", "image/png"}
* )
*/
private $file;
Symfony Messenger Integration Queue uploads for async processing:
use CedrickOka\AttachmentManagerBundle\Message\UploadAttachment;
$bus->dispatch(new UploadAttachment(
$file,
'user_attachments',
['user_id' => 123]
));
Directory Permissions
var/storage/ (or custom path) is writable:
chmod -R 775 var/storage/
oka_attachment_manager.yaml.File Overwrites
file_123abc.jpg).oka_attachment_manager:
unique_filenames: false
Memory Limits
upload_max_filesize or memory_limit.oka_attachment_manager:
chunked_uploads:
enabled: true
chunk_size: 5M
Doctrine Proxy Issues
// ❌ Bad: Triggers proxy loading
public function __construct()
{
$this->attachment = $manager->find(...); // Avoid!
}
CORS for Direct Access
location /storage/ {
add_header 'Access-Control-Allow-Origin' '*';
alias /path/to/storage/;
}
Log Uploads
Enable debug mode in oka_attachment_manager.yaml:
oka_attachment_manager:
debug: true
Check logs for errors in var/log/dev.log.
Validate Storage Test storage connectivity:
php bin/console debug:container CedrickOka\AttachmentManagerBundle\Storage\StorageInterface
Manually trigger a save:
$manager->save($file, 'test_dir');
Common Errors
directory path in AttachmentManager::find().umask in oka_attachment_manager.yaml:
oka_attachment_manager:
umask: 0002
unique_filenames or clean up manually.Custom Metadata
Extend the Attachment entity:
namespace App\Entity;
use CedrickOka\AttachmentManagerBundle\Entity\Attachment as BaseAttachment;
class Attachment extends BaseAttachment
{
/**
* @ORM\Column(type="string", nullable=true)
*/
private $customField;
}
Update the bundle’s resources/config/doctrine/Attachment.orm.xml to include your fields.
Webhooks
Listen for oka_attachment_manager.upload/delete events to trigger external services (e.g., Slack notifications):
$dispatcher->addListener('oka_attachment_manager.upload', function (AttachmentEvent $event) {
$client->post('https://hooks.slack.com/...', [
'text' => sprintf('New attachment: %s', $event->getAttachment()->getFilename())
]);
});
Flysystem Integration Replace the default storage with Flysystem adapters:
services:
CedrickOka\AttachmentManagerBundle\Storage\StorageInterface:
alias: 'oneup_flysystem.attachment_storage'
Configure in oneup_flysystem.yaml:
adapters:
attachment_storage:
local:
directory: %kernel.project_dir%/var/storage
How can I help you explore Laravel packages today?