input('search'); $sort = $request->input('sort'); $tours = Tour::query() ->with([ 'originCity', 'city', 'file', ]) ->withCount('tourDepartures') ->when($search, function ($query) use ($search) { $query->where(function ($query) use ($search) { $query->where('title', 'like', "%{$search}%") ->orWhere('summary', 'like', "%{$search}%") ->orWhereHas('originCity', function ($query) use ($search) { $query->where('name', 'like', "%{$search}%"); }) ->orWhereHas('city', function ($query) use ($search) { $query->where('name', 'like', "%{$search}%"); }); }); }) ->applySort($sort) ->paginate(3) ->withQueryString(); return view('admin.tours.index', compact('title', 'tours')); } public function create() { $title = 'ایجاد تور'; $cities = City::query() ->orderBy('name') ->get(); $countries = Country::query() ->orderBy('name') ->get(); return view('admin.tours.create', compact('title', 'cities', 'countries')); } public function citiesByCountry(Country $country) { $cities = City::query() ->where('county_id', $country->id) ->orderBy('name') ->get(['id', 'name']); return response()->json($cities); } public function store(TourStoreRequest $request) { $data = $request->validated(); try { $startDate = Verta::parseFormat('Y/m/d H:i', $data['start_date'])->toCarbon(); $endDate = Verta::parseFormat('Y/m/d H:i', $data['end_date'])->toCarbon(); } catch (\Throwable $e) { return back() ->withErrors(['start_date' => 'فرمت تاریخ و ساعت وارد شده معتبر نیست.']) ->withInput(); } if ($startDate < now()) { return back() ->withErrors(['start_date' => 'تاریخ حرکت نمی‌تواند قبل از زمان و ساعت جاری باشد.']) ->withInput(); } if ($endDate <= $startDate) { return back() ->withErrors(['end_date' => 'تاریخ برگشت باید بعد از تاریخ حرکت باشد.']) ->withInput(); } DB::beginTransaction(); try { $tour = Tour::create([ 'title' => $data['title'], 'summary' => $data['summary'] ?? null, 'description' => $data['description'] ?? null, 'origin_city_id' => $data['origin_city_id'], 'destination_city_id' => $data['destination_city_id'], 'tour_scope' => $data['tour_scope'], 'tour_mode' => $data['tour_mode'], 'tour_type' => $data['tour_type'], 'views_count' => 0, ]); TourDeparture::create([ 'tour_id' => $tour->id, 'start_date' => $startDate, 'end_date' => $endDate, 'price' => $data['price'], 'capacity' => $data['capacity'], 'status' => 1, ]); if (!empty($data['images'])) { $isDefault = true; foreach ($data['images'] as $index => $image) { $imageName = $tour->id . '_' . time() . rand(11111, 99999) . '.' . $image->extension(); $path = $image->storeAs('tour_images', $imageName, 'public'); $file = File::create([ 'name' => $imageName, 'size' => $image->getSize(), 'original_name' => $image->getClientOriginalName(), 'path' => $path, 'extension' => $image->extension() ]); TourImage::create([ 'tour_id' => $tour->id, 'file_id' => $file->id, 'sort_order' => $index + 1, 'is_default' => $isDefault ]); if ($isDefault) { $isDefault = false; } } } DB::commit(); return redirect() ->route('admin.tours.index') ->with('success', 'تور با موفقیت ایجاد شد.'); } catch (\Throwable $e) { DB::rollBack(); Log::error('خطا در ایجاد تور: ' . $e->getMessage()); return back() ->withErrors(['error' => 'خطایی در ثبت اطلاعات رخ داد. لطفاً دوباره تلاش کنید.']) ->withInput(); } } public function edit(Tour $tour) { $title = 'ویرایش تور'; $tour->load(['originCity', 'city', 'images', 'tourDepartures']); $departure = $tour->tourDepartures->first(); $cities = City::query()->orderBy('name')->get(); $countries = Country::query()->orderBy('name')->get(); $selectedCountryId = null; $destinationCities = $cities; if ($tour->tour_scope === TourScope::INTERNATIONAL && $tour->city) { $selectedCountryId = $tour->city->county_id; $destinationCities = City::query() ->where('county_id', $selectedCountryId) ->orderBy('name') ->get(); } return view('admin.tours.edit', compact( 'title', 'tour', 'departure', 'cities', 'countries', 'selectedCountryId', 'destinationCities' )); } public function update(TourUpdateRequest $request, Tour $tour) { $data = $request->validated(); try { $startDate = Verta::parse($data['start_date'])->datetime(); $endDate = Verta::parse($data['end_date'])->datetime(); } catch (\Throwable $e) { return back() ->withErrors(['start_date' => 'فرمت تاریخ و ساعت وارد شده معتبر نیست.']) ->withInput(); } if ($endDate <= $startDate) { return back() ->withErrors(['end_date' => 'تاریخ برگشت باید بعد از تاریخ حرکت باشد.']) ->withInput(); } DB::beginTransaction(); try { $tour->update([ 'title' => $data['title'], 'summary' => $data['summary'] ?? null, 'description' => $data['description'] ?? null, 'origin_city_id' => $data['origin_city_id'], 'destination_city_id' => $data['destination_city_id'], 'tour_scope' => $data['tour_scope'], 'tour_mode' => $data['tour_mode'], 'tour_type' => $data['tour_type'], ]); $departure = $tour->tourDepartures()->first(); if ($departure) { $departure->update([ 'start_date' => $startDate, 'end_date' => $endDate, 'price' => $data['price'], 'capacity' => $data['capacity'], ]); } else { TourDeparture::create([ 'tour_id' => $tour->id, 'start_date' => $startDate, 'end_date' => $endDate, 'price' => $data['price'], 'capacity' => $data['capacity'], 'status' => 1, ]); } if (!empty($data['images'])) { $hasDefault = $tour->images()->where('is_default', true)->exists(); $nextSort = (int)$tour->images()->max('sort_order'); foreach ($data['images'] as $index => $image) { $imageName = $tour->id . '_' . time() . rand(11111, 99999) . '.' . $image->extension(); $path = $image->storeAs('tour_images', $imageName, 'public'); $file = File::create([ 'name' => $imageName, 'size' => $image->getSize(), 'original_name' => $image->getClientOriginalName(), 'path' => $path, 'extension' => $image->extension(), ]); TourImage::create([ 'tour_id' => $tour->id, 'file_id' => $file->id, 'sort_order' => $nextSort + $index + 1, 'is_default' => !$hasDefault && $index === 0, ]); } } DB::commit(); return redirect() ->route('admin.tours.index') ->with('success', 'تور با موفقیت ویرایش شد.'); } catch (\Exception $exception) { DB::rollBack(); Log::error('خطا در ویرایش تور: ' . $exception->getMessage()); return back() ->withErrors(['error' => 'خطایی در ثبت اطلاعات رخ داد. لطفاً دوباره تلاش کنید.']) ->withInput(); } } public function trash() { $title = 'تورهای حذف شده'; $tours = Tour::onlyTrashed() ->with([ 'originCity', 'city', 'coverImage.file', ]) ->orderByDesc('deleted_at') ->paginate(4); return view('admin.tours.trash', compact('title', 'tours')); } public function destroy(Tour $tour) { $tour->delete(); return redirect() ->route('admin.tours.index') ->with('success', 'تور با موفقیت به سطل زباله منتقل شد.'); } public function restore( $id) { $tour = Tour::onlyTrashed()->findOrFail($id); $tour->restore(); return redirect() ->route('admin.tours.trash.index') ->with('success', 'تور با موفقیت بازگردانی شد.'); } public function forceDelete($id) { $tour = Tour::onlyTrashed()->findOrFail($id); $tour->forceDelete(); return redirect() ->route('admin.tours.trash.index') ->with('success', 'تور به‌صورت دائمی حذف شد.'); } }