Installation:
composer require laravelir/attachmentable
php artisan attachmentable:install
php artisan migrate
Ensure AttachmentableServiceProvider is registered in config/app.php.
Basic Setup:
Attachmentable trait to models needing attachments (e.g., Post).Attachmentorable trait to models that own attachments (e.g., User).First Use Case:
Attach a file to a Post owned by a User:
$user = User::find(1);
$post = $user->posts()->create(['title' => 'My Post']);
$post->attachFile('path/to/file.pdf', 'document'); // 'document' is the attachment type
config/attachmentable.php (disk, validation rules, etc.).database/migrations/ for attachmentable tables.Attachmentable and Attachmentorable methods in Traits/.Attaching Files:
// Single file
$model->attachFile($filePath, $type, ['custom' => 'metadata']);
// Multiple files (array of paths)
$model->attachFiles(['path1.jpg', 'path2.jpg'], 'images');
Retrieving Attachments:
// Get all attachments for a model
$attachments = $model->attachments;
// Get by type
$documents = $model->attachments->where('type', 'document');
// Get URL of a specific attachment
$url = $model->attachments->first()->url;
Deleting Attachments:
$model->detachFile($attachmentId); // By ID
$model->detachFiles(['type' => 'images']); // By type/query
$model->detachAllFiles(); // All attachments
config/attachmentable.php to customize allowed file types/sizes:
'validation' => [
'types' => ['document' => ['pdf', 'doc'], 'image' => ['jpg', 'png']],
'max_size' => 10240, // 10MB
],
attachment.attached/attachment.detached:
event(new AttachmentAttached($model, $attachment));
Attachmentorable on models like User, Project, etc., to centralize attachment management.php artisan vendor:publish --provider="Laravelir\Attachmentable\Providers\AttachmentableServiceProvider"
Then update config/attachmentable.php:
'disk' => 's3',
use Laravelir\Attachmentable\Traits\SoftDeletesAttachment;
class Post extends Model {
use SoftDeletesAttachment;
}
Ownership Confusion:
Attachmentorable is on the owner model (e.g., User), not the attachment target (e.g., Post).File Path Handling:
storage_path('app/uploads/file.pdf') or public_path('uploads/file.pdf').Migration Conflicts:
attachmentable:install fails, manually check for duplicate attachments table migrations.Disk Permissions:
chmod -R 775 storage/app/public
Attachment Not Found:
attachable_id and attachable_type in the attachments table match the owner model’s primary key and class name.\DB::table('attachments')->where('attachable_id', $user->id)->where('attachable_type', 'App\Models\User')->get();
File Not Uploaded:
disk config and ensure the file exists at the specified path.\Log::debug('Attachment path:', [$filePath, config('attachmentable.disk')]);
Custom Attachment Model:
Attachment model:
php artisan vendor:publish --tag=attachmentable-migrations
mime_type, size) to the attachments table.Custom Storage Logic:
storeFile method in a service class:
namespace App\Services;
use Laravelir\Attachmentable\Attachment;
class CustomAttachmentService {
public function storeFile(Attachment $attachment, $filePath) {
// Custom logic (e.g., rename files, process before storage)
return $attachment->store($filePath);
}
}
AttachmentableServiceProvider.API Responses:
public function getAttachmentUrlAttribute() {
return $this->url . '?t=' . time(); // Cache-busting
}
Batch Processing:
Use chunk() for large attachment operations to avoid memory issues:
$user->posts()->chunk(100, function ($posts) {
foreach ($posts as $post) {
$post->attachFiles([...], 'images');
}
});
Testing: Mock the filesystem in tests:
$this->partialMock(\Illuminate\Filesystem\Filesystem::class, function ($mock) {
$mock->shouldReceive('exists')->andReturnTrue();
$mock->shouldReceive('put')->andReturnTrue();
});
Performance:
attachable_id and attachable_type in the attachments table for large datasets.with() to eager-load attachments:
$user->load('attachments');
How can I help you explore Laravel packages today?