media()->max('sort_order'); if ($request->hasFile('photos')) { foreach ((array) $request->file('photos') as $file) { if ($file instanceof UploadedFile && $file->isValid()) { $this->storeFile($business, $listing, $file, 'photo', null, ++$sort); $count++; } } } if ($request->hasFile('documents')) { foreach ((array) $request->file('documents') as $file) { if ($file instanceof UploadedFile && $file->isValid()) { $this->storeFile($business, $listing, $file, 'document', null, ++$sort); $count++; } } } foreach (['photo', 'document'] as $type) { $single = $type === 'photo' ? 'photo' : 'document'; if ($request->hasFile($single) && $request->file($single)->isValid()) { $this->storeFile($business, $listing, $request->file($single), $type, null, ++$sort); $count++; } } return $count; } public function storeFile( Business $business, Listing $listing, UploadedFile $file, string $mediaType, ?string $caption = null, int $sortOrder = 0 ): ListingMedia { if (config('app.env') === 'demo') { throw new \RuntimeException('آپلود در محیط دمو غیرفعال است.'); } $fileType = $mediaType === 'photo' ? 'image' : 'document'; if ($fileType === 'image' && ! str_starts_with((string) $file->getMimeType(), 'image/')) { throw new \RuntimeException('فایل تصویر نامعتبر است.'); } if ($fileType === 'document') { $allowed = array_keys(config('constants.document_upload_mimes_types', [])); if ($allowed && ! in_array($file->getMimeType(), $allowed, true)) { throw new \RuntimeException('نوع فایل مجاز نیست.'); } } if ($file->getSize() > config('constants.document_size_limit')) { throw new \RuntimeException('حجم فایل بیش از حد مجاز است.'); } $original = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME); $extension = $file->getClientOriginalExtension(); $stored = time().'_'.Str::slug($original).($extension ? '.'.$extension : ''); if (! $file->storeAs(self::UPLOAD_DIR, $stored)) { throw new \RuntimeException('آپلود فایل ناموفق بود.'); } return ListingMedia::create([ 'business_id' => $business->id, 'listing_id' => $listing->id, 'media_type' => $mediaType, 'path' => $stored, 'caption' => $caption, 'sort_order' => $sortOrder, ]); } public function deleteMedia(ListingMedia $media): void { $fullPath = public_path('uploads/'.self::UPLOAD_DIR.'/'.$media->path); if (is_file($fullPath)) { @unlink($fullPath); } $media->delete(); } public function publicUrl(ListingMedia $media): string { return asset('uploads/'.self::UPLOAD_DIR.'/'.$media->path); } }