$machine->id, 'files' => $request->allFiles() ]); try { $images = $machine->images ?? []; // 處理 3 個索引位置的圖片 for ($i = 0; $i < 3; $i++) { // 先處理刪除標記 if ($request->input("delete_photo_{$i}") === '1') { if (isset($images[$i])) { unset($images[$i]); } } // 再處理檔案上傳(若有上傳會覆蓋掉刪除邏輯或原有的圖) $fieldName = "machine_image_{$i}"; if ($request->hasFile($fieldName)) { $file = $request->file($fieldName); // 轉為 WebP 格式與保存 $path = $this->storeAsWebp($file, "machines/{$machine->id}"); $images[$i] = $path; Log::info("Machine image uploaded at slot {$i}", ['path' => $path]); } } // 過濾掉 null 並重新整理索引,但這裡我們希望保持 3 個槽位的概念 // 如果用戶想保持順序,我們就直接儲存 ksort($images); $machine->update([ 'images' => $images, 'updater_id' => auth()->id(), ]); return back()->with('success', __('Machine images updated successfully.')); } catch (\Exception $e) { Log::error('Machine Photo Update Failed', [ 'machine_id' => $machine->id, 'error' => $e->getMessage() ]); return back()->with('error', __('Failed to update machine images: ') . $e->getMessage()); } } /** * 將圖片轉換為 WebP 並儲存 */ protected function storeAsWebp($file, $directory): string { $extension = $file->getClientOriginalExtension(); $filename = uniqid() . '.webp'; $path = "{$directory}/{$filename}"; // 讀取原始圖片 $imageType = exif_imagetype($file->getRealPath()); switch ($imageType) { case IMAGETYPE_JPEG: $source = imagecreatefromjpeg($file->getRealPath()); break; case IMAGETYPE_PNG: $source = imagecreatefrompng($file->getRealPath()); break; case IMAGETYPE_WEBP: $source = imagecreatefromwebp($file->getRealPath()); break; default: // 如果格式不支援,直接存 return $file->storeAs($directory, $file->hashName(), 'public'); } if (!$source) { return $file->storeAs($directory, $file->hashName(), 'public'); } // 確保支援真彩色(解決 palette image 問題) if (!imageistruecolor($source)) { imagepalettetotruecolor($source); } // 捕捉輸出 ob_start(); imagewebp($source, null, 80); $content = ob_get_clean(); imagedestroy($source); Storage::disk('public')->put($path, $content); return $path; } }