136 lines
3.5 KiB
PHP
136 lines
3.5 KiB
PHP
<?php
|
|
|
|
namespace App\Models\Concerns;
|
|
|
|
use App\Models\Attachment;
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
trait HasAttachments
|
|
{
|
|
/**
|
|
* Get all attachments for this model.
|
|
*/
|
|
public function attachments()
|
|
{
|
|
return $this->morphMany(Attachment::class, 'attachable');
|
|
}
|
|
|
|
/**
|
|
* Get receipts only.
|
|
*/
|
|
public function receipts()
|
|
{
|
|
return $this->morphMany(Attachment::class, 'attachable')
|
|
->where('category', 'receipt');
|
|
}
|
|
|
|
/**
|
|
* Get documents only.
|
|
*/
|
|
public function documents()
|
|
{
|
|
return $this->morphMany(Attachment::class, 'attachable')
|
|
->where('category', 'document');
|
|
}
|
|
|
|
/**
|
|
* Upload and attach a file.
|
|
*/
|
|
public function attachFile(
|
|
UploadedFile $file,
|
|
string $category = 'receipt',
|
|
?string $description = null,
|
|
?string $folder = null
|
|
): Attachment {
|
|
$folder = $folder ?? $this->getAttachmentFolder();
|
|
$filePath = $file->store($folder, 'public');
|
|
|
|
return $this->attachments()->create([
|
|
'tenant_id' => session('tenant_id') ?? $this->tenant_id,
|
|
'user_id' => Auth::id(),
|
|
'file_name' => $file->getClientOriginalName(),
|
|
'file_path' => $filePath,
|
|
'mime_type' => $file->getMimeType(),
|
|
'file_size' => $file->getSize(),
|
|
'category' => $category,
|
|
'description' => $description,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Upload multiple files at once.
|
|
*/
|
|
public function attachFiles(array $files, string $category = 'receipt', ?string $folder = null): array
|
|
{
|
|
$attachments = [];
|
|
foreach ($files as $file) {
|
|
if ($file instanceof UploadedFile && $file->isValid()) {
|
|
$attachments[] = $this->attachFile($file, $category, null, $folder);
|
|
}
|
|
}
|
|
return $attachments;
|
|
}
|
|
|
|
/**
|
|
* Detach (soft delete) an attachment by ID.
|
|
*/
|
|
public function detachFile(int $attachmentId): bool
|
|
{
|
|
$attachment = $this->attachments()->find($attachmentId);
|
|
|
|
if ($attachment) {
|
|
$attachment->delete();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Migrate legacy receipt_path to the attachment system.
|
|
*/
|
|
public function migrateLegacyReceipt(): ?Attachment
|
|
{
|
|
$receiptPath = $this->getOriginal('receipt_path') ?? $this->receipt_path ?? null;
|
|
|
|
if (!$receiptPath) {
|
|
return null;
|
|
}
|
|
|
|
// Check if already migrated
|
|
$exists = $this->attachments()
|
|
->where('file_path', $receiptPath)
|
|
->exists();
|
|
|
|
if ($exists) {
|
|
return null;
|
|
}
|
|
|
|
if (!Storage::disk('public')->exists($receiptPath)) {
|
|
return null;
|
|
}
|
|
|
|
return $this->attachments()->create([
|
|
'tenant_id' => $this->tenant_id,
|
|
'user_id' => null,
|
|
'file_name' => basename($receiptPath),
|
|
'file_path' => $receiptPath,
|
|
'mime_type' => Storage::disk('public')->mimeType($receiptPath),
|
|
'file_size' => Storage::disk('public')->size($receiptPath),
|
|
'category' => 'receipt',
|
|
'description' => null,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get the default storage folder for this model's attachments.
|
|
*/
|
|
protected function getAttachmentFolder(): string
|
|
{
|
|
$className = strtolower(class_basename($this));
|
|
return "attachments/{$className}";
|
|
}
|
|
}
|