getClientOriginalExtension()) ?: 'jpg'; $ext = in_array($ext, ['jpg', 'jpeg', 'png', 'webp'], true) ? $ext : 'jpg'; $originalRelPath = "photos/{$uuid}.{$ext}"; $thumbRelPath = "photos/thumbs/{$uuid}.{$ext}"; $annotatedRelPath = "photos/annotated/{$uuid}.jpg"; $image = Image::decode($file->getContent()); $image->orient(); $image->scaleDown(width: self::MAX_ORIGINAL_PX, height: self::MAX_ORIGINAL_PX); $disk = Storage::disk('public'); $encodedOriginal = (string) $image->encodeUsingMediaType($this->mediaType($ext), quality: self::JPEG_QUALITY); $disk->put($originalRelPath, $encodedOriginal); $image->scaleDown(width: self::THUMB_WIDTH_PX); $disk->put($thumbRelPath, (string) $image->encodeUsingMediaType($this->mediaType($ext), quality: self::JPEG_QUALITY)); $annotationService = app(PhotoAnnotationService::class); try { $annotatedContent = $annotationService->annotate($encodedOriginal, $job); $disk->put($annotatedRelPath, $annotatedContent); } catch (\Throwable $e) { Log::warning('Photo annotation failed, continuing without annotated version', [ 'job_id' => $job->id, 'error' => $e->getMessage(), ]); $annotatedRelPath = null; } Log::info('Photo stored', [ 'job_id' => $job->id, 'file' => $originalRelPath, 'thumb' => $thumbRelPath, 'annotated' => $annotatedRelPath, ]); return JobPhoto::create([ 'job_id' => $job->id, 'file_path' => $originalRelPath, 'thumbnail_path' => $thumbRelPath, 'annotated_path' => $annotatedRelPath, ]); } public static function canAddPhoto(Job $job): bool { return $job->jobPhotos()->count() < self::MAX_PHOTOS_PER_JOB; } private function mediaType(string $ext): string { return match ($ext) { 'png' => 'image/png', 'webp' => 'image/webp', default => 'image/jpeg', }; } }