validate([ 'attachable_type' => 'required|string', 'attachable_id' => 'required|integer', 'files' => 'required|array|min:1', 'files.*' => 'file|mimes:jpg,jpeg,png,pdf,doc,docx,xls,xlsx|max:10240', 'category' => 'nullable|string|in:receipt,document,photo,other', ]); // Resolve the model $allowedTypes = [ 'App\\Models\\Expense', 'App\\Models\\PettyCash', 'App\\Models\\Project', 'App\\Models\\Task', 'App\\Models\\Letter', 'App\\Models\\Document', ]; if (!in_array($validated['attachable_type'], $allowedTypes)) { return response()->json([ 'success' => false, 'message' => __('attachment.invalid_model_type'), ], 422); } $model = $validated['attachable_type']::find($validated['attachable_id']); if (!$model) { return response()->json([ 'success' => false, 'message' => __('attachment.model_not_found'), ], 404); } // Authorize if (method_exists($model, 'update')) { $this->authorize('update', $model); } $category = $validated['category'] ?? 'receipt'; $attachments = $model->attachFiles($validated['files'], $category); return response()->json([ 'success' => true, 'message' => __('attachment.uploaded_successfully'), 'attachments' => collect($attachments)->map(fn ($a) => [ 'id' => $a->id, 'file_name' => $a->file_name, 'file_size' => $a->formatted_size, 'mime_type' => $a->mime_type, 'category' => $a->category, 'url' => $a->url, 'is_image' => $a->is_image, 'icon' => $a->icon, 'created_at' => $a->created_at?->format('Y-m-d H:i'), ]), ]); } /** * Delete an attachment. */ public function destroy(Attachment $attachment) { $this->authorize('delete', $attachment); $attachment->delete(); return response()->json([ 'success' => true, 'message' => __('attachment.deleted_successfully'), ]); } /** * Download an attachment. */ public function download(Attachment $attachment) { $this->authorize('view', $attachment); if (!\Illuminate\Support\Facades\Storage::disk('public')->exists($attachment->file_path)) { abort(404); } return \Illuminate\Support\Facades\Storage::disk('public') ->download($attachment->file_path, $attachment->file_name); } }