diff --git a/app/Http/Controllers/TourController.php b/app/Http/Controllers/TourController.php index 1a40970..a2ff745 100644 --- a/app/Http/Controllers/TourController.php +++ b/app/Http/Controllers/TourController.php @@ -8,92 +8,139 @@ use Illuminate\Http\Request; class TourController extends Controller { - //لیست/آرشیو تورها با قابلیت فیلتر + /** + * لیست / آرشیو تورها با قابلیت فیلتر + */ public function index(Request $request) { $query = Tour::query() - ->where('status', 1) // فقط تورهای فعال - ->with(['travelType', 'flights']); // بارگذاری زودهنگام برای جلوگیری از N+1 + ->where('status', 1) + ->with(['travelType', 'flights']); - // فیلتر بر اساس نوع سفر (travel_type_id) + // فیلتر بر اساس نوع سفر if ($request->filled('travel_type')) { - $query->where('travel_type_id', $request->input('travel_type')); + $query->where( + 'travel_type_id', + $request->input('travel_type') + ); } - // فیلتر جستجوی متنی (عنوان) + // جستجو بر اساس عنوان if ($request->filled('q')) { - $query->where('title', 'like', '%' . $request->input('q') . '%'); + $query->where( + 'title', + 'like', + '%' . $request->input('q') . '%' + ); } - // فیلتر بازه قیمتی + // حداقل قیمت if ($request->filled('price_min')) { - $query->where('price', '>=', $request->input('price_min')); + $query->where( + 'price', + '>=', + $request->input('price_min') + ); } + + // حداکثر قیمت if ($request->filled('price_max')) { - $query->where('price', '<=', $request->input('price_max')); + $query->where( + 'price', + '<=', + $request->input('price_max') + ); } - // مرتبسازی بر اساس قیمت (اگر خواستی) + // مرتبسازی if ($request->filled('sort')) { - $query->orderBy('price', $request->input('sort') === 'desc' ? 'desc' : 'asc'); + $query->orderBy( + 'price', + $request->input('sort') === 'desc' + ? 'desc' + : 'asc' + ); } else { - $query->latest(); // جدیدترینها اول + $query->latest(); } - $tours = $query->paginate(12); - $travelTypes = TravelType::where('status', 1)->get(); // برای منوی فیلتر + // صفحهبندی + $tours = $query->paginate(12)->withQueryString(); - return view('tours.index', compact('tours', 'travelTypes')); + // انواع سفر + $travelTypes = TravelType::where('status', 1)->get(); + + return view( + 'tours.index', + compact('tours', 'travelTypes') + ); } -//حذف فیلترها و بازگشت به آرشیو کامل + + /** + * حذف فیلترها + */ public function removeFilters() { - // ساده: فقط برگشت به آرشیو بدون هیچ پارامتری. - // (اگر از کوکی/سشن استفاده میکنی، اینجا آنها را هم پاک کن) return redirect()->route('tours.index'); } - //صفحهی جزئیات یک تور + + /** + * نمایش جزئیات یک تور + */ public function show(Tour $tour) { - // فقط اگر تور غیرفعال بود از دسترسی جلوگیری کن (مثلاً ریدایرکت) - if ($tour->status == 0) { + // فقط تورهای فعال قابل مشاهده باشند + if ($tour->status != 1) { abort(404); } - // بارگذاری پروازها + نوع سفر + نظرات تاییدشده + // افزایش تعداد بازدید + $tour->increment('views_count'); + + // بارگذاری اطلاعات مورد نیاز $tour->load([ 'flights', 'travelType', - // توجه: این رابطه را باید در مدل Tour تعریف کنی (morphMany) - 'comments' => function ($q) { - $q->where('status', 1)->latest(); - } ]); - // تورهای پیشنهادی مرتبط (همدسته) برای نمایش کنار صفحه + // تورهای مرتبط $relatedTours = Tour::where('status', 1) ->where('travel_type_id', $tour->travel_type_id) ->where('id', '!=', $tour->id) + ->latest() ->limit(4) ->get(); - return view('tours.show', compact('tour', 'relatedTours')); + return view( + 'tours.show', + compact('tour', 'relatedTours') + ); } - //جستجوی پیشنهادی (Typeahead / AJAX) — خروجی JSON + /** + * جستجوی پیشنهادی + */ public function searchSuggestion(Request $request) { $q = $request->input('q', ''); $tours = Tour::where('status', 1) - ->where('title', 'like', '%' . $q . '%') + ->where( + 'title', + 'like', + '%' . $q . '%' + ) ->limit(8) - ->get(['id', 'title', 'slug', 'price']); + ->get([ + 'id', + 'title', + 'slug', + 'price' + ]); return response()->json($tours); } } - diff --git a/app/Http/Requests/TourStoreRequest.php b/app/Http/Requests/TourStoreRequest.php index 0ee6785..fd58f90 100644 --- a/app/Http/Requests/TourStoreRequest.php +++ b/app/Http/Requests/TourStoreRequest.php @@ -23,60 +23,60 @@ class TourStoreRequest extends FormRequest public function rules(): array { return [ - 'travel_type_id' => - [ - 'required', - 'exists:travel_types,id' - ], +// 'travel_type_id' => +// [ +// 'nullable', +// 'exists:travel_types,id' +// ], 'title' => [ 'required', 'string', 'max:200' ], - 'slug' => - ['required', 'string', - 'max:200', - 'regex:/^[a-z0-9-]+$/', - 'unique:tours,slug,' . $this->tour - ], +// 'slug' => +// ['nullable', 'string', +// 'max:200', +// 'regex:/^[a-z0-9-]+$/', +// 'unique:tours,slug,' . $this->tour +// ], - 'description' => - [ - 'required', - 'string' - ], - 'duration' => - [ - 'required', - 'string', - 'max:50' - ], - 'start_date' => - [ - 'required', - 'date' - ], - 'end_date' => - [ - 'required', - 'date', - 'after_or_equal:start_date' - ], +// 'description' => +// [ +// 'nullable', +// 'string' +// ], +// 'duration' => +// [ +// 'nullable', +// 'string', +// 'max:50' +// ], +// 'start_date' => +// [ +// 'nullable', +// 'date' +// ], +// 'end_date' => +// [ +// 'nullable', +// 'date', +// 'after_or_equal:start_date' +// ], 'price' => [ 'required', 'numeric', 'min:0' ], - 'capacity' => - [ - 'required', - 'integer', - 'min:0'], +// 'capacity' => +// [ +// 'nullable', +// 'integer', +// 'min:0'], 'images' => [ - 'required', + 'nullable', 'array' ], 'images.*' => diff --git a/resources/views/admin/tours/create.blade.php b/resources/views/admin/tours/create.blade.php index e0547e3..30ecce9 100644 --- a/resources/views/admin/tours/create.blade.php +++ b/resources/views/admin/tours/create.blade.php @@ -2,61 +2,63 @@ @section('content')