141 lines
3.9 KiB
PHP
141 lines
3.9 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\FeedbackAttachment;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Facades\URL;
|
|
use Illuminate\Support\Str;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
class FileService
|
|
{
|
|
protected array $allowedMimes = [
|
|
'image/jpeg',
|
|
'image/jpg',
|
|
'image/png',
|
|
'application/pdf',
|
|
'video/mp4',
|
|
'video/quicktime',
|
|
'video/mov',
|
|
];
|
|
|
|
protected int $maxSize = 20480;
|
|
|
|
protected string $defaultDisk = 'public';
|
|
|
|
/**
|
|
* Upload files and create FeedbackAttachment records.
|
|
*/
|
|
public function upload(
|
|
array $files,
|
|
string $collection,
|
|
Model $model,
|
|
int $userId,
|
|
?int $interactionId = null,
|
|
string $disk = 'public',
|
|
): array {
|
|
$records = [];
|
|
|
|
foreach ($files as $file) {
|
|
if (! $file instanceof UploadedFile || ! $file->isValid()) {
|
|
continue;
|
|
}
|
|
|
|
$this->validate($file);
|
|
|
|
$path = $file->store('feedback-attachments', $disk);
|
|
|
|
$records[] = FeedbackAttachment::create([
|
|
'uuid' => (string) Str::uuid(),
|
|
'feedback_id' => $model instanceof \App\Models\Feedback ? $model->id : null,
|
|
'feedback_interaction_id' => $interactionId,
|
|
'user_id' => $userId,
|
|
'name' => $file->getClientOriginalName(),
|
|
'path' => $path,
|
|
'disk' => $disk,
|
|
'collection' => $collection,
|
|
'mime_type' => $file->getMimeType(),
|
|
'size' => $file->getSize(),
|
|
]);
|
|
}
|
|
|
|
return $records;
|
|
}
|
|
|
|
/**
|
|
* Generate a temporary URL for a file on a private disk.
|
|
*/
|
|
public function getTemporaryUrl(FeedbackAttachment $attachment, int $minutes = 10): string
|
|
{
|
|
if ($attachment->disk === 'public') {
|
|
return asset('storage/' . $attachment->path);
|
|
}
|
|
|
|
return Storage::disk($attachment->disk)->temporaryUrl(
|
|
$attachment->path,
|
|
now()->addMinutes($minutes),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Generate a preview (inline) URL vs download URL.
|
|
*/
|
|
public function getPreviewUrl(FeedbackAttachment $attachment, int $minutes = 60): string
|
|
{
|
|
return $this->getTemporaryUrl($attachment, $minutes);
|
|
}
|
|
|
|
/**
|
|
* Check if attachment is an image (for inline preview).
|
|
*/
|
|
public function isImage(FeedbackAttachment $attachment): bool
|
|
{
|
|
return in_array($attachment->mime_type, ['image/jpeg', 'image/jpg', 'image/png']);
|
|
}
|
|
|
|
/**
|
|
* Get all attachments of a specific collection for a model.
|
|
*/
|
|
public function getByCollection(Model $model, string $collection)
|
|
{
|
|
$query = FeedbackAttachment::byCollection($collection);
|
|
|
|
if ($model instanceof \App\Models\Feedback) {
|
|
$query->where('feedback_id', $model->id);
|
|
}
|
|
|
|
return $query->orderBy('created_at', 'desc')->get();
|
|
}
|
|
|
|
/**
|
|
* Check if a model has at least one attachment in the given collection.
|
|
*/
|
|
public function hasCollection(Model $model, string $collection): bool
|
|
{
|
|
return $this->getByCollection($model, $collection)->isNotEmpty();
|
|
}
|
|
|
|
/**
|
|
* Validate file type and size.
|
|
*/
|
|
protected function validate(UploadedFile $file): void
|
|
{
|
|
$errors = [];
|
|
|
|
if (! in_array($file->getMimeType(), $this->allowedMimes)) {
|
|
$errors['file'] = __('feedback.file_type_not_allowed', ['type' => $file->getMimeType()]);
|
|
}
|
|
|
|
if ($file->getSize() > $this->maxSize * 1024) {
|
|
$errors['file'] = __('feedback.file_size_exceeds', ['max' => $this->maxSize]);
|
|
}
|
|
|
|
if (! empty($errors)) {
|
|
throw ValidationException::withMessages($errors);
|
|
}
|
|
}
|
|
}
|