feat: implement favorites for tours and flights

This commit is contained in:
2026-08-18 18:38:03 +03:30
parent b1caa3fbfd
commit 7fd710bd99
7 changed files with 311 additions and 36 deletions
@@ -3,33 +3,101 @@
namespace App\Http\Controllers\Account;
use App\Http\Controllers\Controller;
use App\Models\Favorite;
use App\Models\Flight;
use App\Models\Tour;
use Illuminate\Http\Request;
class FavoriteController extends Controller
{
public function index()
{
$title = 'علاقه مندی های من';
$favorites = Favorite::query()
->where('user_id', auth()->id())
->with([
'tour.file',
'tour.originCity',
'tour.city',
'tour.tourDepartures',
'flight.airline',
'flight.origin_airport.city',
'flight.airport.city',
])
->orderByDesc('created_at')
->get();
return view('account.favorite-list', compact('title','favorites'));
}
public function toggleTour()
public function removeTour($tourId)
{
Favorite::where('user_id', auth()->id())
->where('tour_id', $tourId)
->delete();
return back()->with('success', 'با موفقیت حذف شد.');
}
public function toggleTour(Tour $tour)
{
if (!auth()->check()) {
return back()->with('error', 'برای افزودن به علاقهمندی ها ابتدا وارد شوید.');
}
$userId = auth()->id();
$favorite = Favorite::where('user_id', $userId)
->where('tour_id', $tour->id)
->first();
if ($favorite) {
$favorite->delete();
$message = 'از علاقهمندیها حذف شد.';
} else {
Favorite::create([
'user_id' => $userId,
'tour_id' => $tour->id,
]);
$message = 'به علاقهمندیها اضافه شد.';
}
return back()->with('success', $message);
}
public function removeTour()
public function removeFlight(Flight $flight)
{
Favorite::where('user_id', auth()->id())
->where('flight_id', $flight->id)
->delete();
return back()->with('success', 'پرواز با موفقیت از علاقه‌مندی‌ها حذف شد.');
}
public function toggleFlight()
public function toggleFlight(Flight $flight)
{
if (!auth()->check()) {
return back()->with('error', 'برای افزودن پرواز به علاقه‌مندی‌ها ابتدا وارد شوید.');
}
$userId = auth()->id();
$favorite = Favorite::where('user_id', $userId)
->where('flight_id', $flight->id)
->first();
if ($favorite) {
$favorite->delete();
$message = 'پرواز از علاقه‌مندی‌ها حذف شد.';
} else {
Favorite::create([
'user_id' => $userId,
'flight_id' => $flight->id,
]);
$message = 'پرواز به لیست علاقه‌مندی‌ها اضافه شد.';
}
return back()->with('success', $message);
}
public function removeFlight()
{
}
}
@@ -27,6 +27,7 @@ class ProfileController extends Controller
public function comments()
{
$title = 'کامنت های من';
$comments = auth()->user()
->comments()
->with([
@@ -36,8 +37,6 @@ class ProfileController extends Controller
->orderByDesc('created_at')
->paginate(10);
return view('account.comments' , compact('comments'));
return view('account.comments' , compact('title','comments'));
}
}
+13
View File
@@ -37,4 +37,17 @@ class Favorite extends Model
'tour_id',
'flight_id'
];
public function flight()
{
return $this->belongsTo(Flight::class, 'flight_id');
}
public function tour()
{
return $this->belongsTo(Tour::class, 'tour_id');
}
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
}
@@ -0,0 +1,159 @@
@extends('account.layouts.app')
@section('account-content')
<div class="user-profile-wrapper">
@if (session('success'))
<div class="col-lg-12 mb-3">
<div class="alert alert-success">
{{ session('success') }}
</div>
</div>
@endif
@if (session('error'))
<div class="col-lg-12 mb-3">
<div class="alert alert-danger">
{{ session('error') }}
</div>
</div>
@endif
<div class="col-lg-12">
<div class="user-profile-card">
<div class="d-flex justify-content-between align-items-center mb-4">
<h4 class="user-profile-card-title mb-0">
<i class="far fa-heart text-danger me-1"></i>
علاقه‌مندی‌های من
</h4>
<span class="badge bg-primary">
{{ method_exists($favorites, 'total') ? $favorites->total() : $favorites->count() }} مورد
</span>
</div>
<div class="row mt-30">
@forelse($favorites as $favorite)
@if($favorite->tour)
<div class="col-md-6 col-lg-4 mb-4">
<div class="hotel-item h-100 d-flex flex-column justify-content-between">
<div class="hotel-img position-relative">
<span class="badge bg-info position-absolute top-0 start-0 m-2">تور</span>
<img src="{{ $favorite->tour->file ? asset($favorite->tour->file->path) : asset('assets/img/tour/default.jpg') }}"
alt="{{ $favorite->tour->title }}"
style="height: 200px; width: 100%; object-fit: cover;">
<form method="POST" action="{{ route('accounts.favorites.tour.toggle', $favorite->tour->id) }}" class="d-inline">
@csrf
<button type="submit" class="add-wishlist border-0 bg-transparent" title="حذف از علاقه‌مندی">
<i class="fas fa-heart text-danger"></i>
</button>
</form>
</div>
<div class="hotel-content d-flex flex-column flex-grow-1 justify-content-between">
<div>
<h4 class="hotel-title">
<a href="{{ route('tours.show', $favorite->tour->id) }}">
{{ $favorite->tour->title }}
</a>
</h4>
<p><i class="far fa-location-dot"></i>
{{ $favorite->tour->originCity->name ?? 'مبدأ' }}
<i class="fas fa-arrow-right-long"></i>
{{ $favorite->tour->city?->name ?? 'مقصد' }}
</p>
</div>
<div class="hotel-bottom mt-3">
<div class="hotel-price">
<span class="hotel-price-amount">
{{ number_format(optional($favorite->tour->tourDepartures->first())->price ?? 0) }} تومان
</span>
</div>
<div class="hotel-text-btn">
<a href="{{ route('tours.show', $favorite->tour->id) }}">
نمایش جزئیات <i class="fas fa-arrow-left"></i>
</a>
</div>
</div>
</div>
</div>
</div>
@elseif($favorite->flight)
<div class="col-md-6 col-lg-4 mb-4">
<div class="hotel-item h-100 d-flex flex-column justify-content-between">
<div class="hotel-img position-relative">
<span class="badge bg-primary position-absolute top-0 start-0 m-2">پرواز</span>
<img src="{{ $favorite->flight->file ? asset($favorite->flight->file->path) : asset('assets/img/flight/default.jpg') }}"
alt="{{ $favorite->flight->flight_number }}"
style="height: 200px; width: 100%; object-fit: cover;">
<form method="POST" action="{{ route('accounts.favorites.flight.toggle', $favorite->flight->id) }}" class="d-inline">
@csrf
<button type="submit" class="add-wishlist border-0 bg-transparent" title="حذف از علاقه‌مندی">
<i class="fas fa-heart text-danger"></i>
</button>
</form>
</div>
<div class="hotel-content d-flex flex-column flex-grow-1 justify-content-between">
<div>
<h4 class="hotel-title">
<a href="{{ route('flights.show', $favorite->flight->id) }}">
{{ $favorite->flight->origin_airport?->city?->name ?? 'مبدأ' }} به {{ $favorite->flight->airport?->city?->name ?? 'مقصد' }}
</a>
</h4>
<p class="text-muted mb-1">
<i class="far fa-plane"></i>
{{ $favorite->flight->airline?->name }} (شماره: {{ $favorite->flight->flight_number }})
</p>
@if($favorite->flight->departure_time)
<p class="small text-muted mb-0">
<i class="far fa-calendar-alt"></i>
{{ \Hekmatinasser\Verta\Verta::instance($favorite->flight->departure_time)->format('Y/m/d - H:i') }}
</p>
@endif
</div>
<div class="hotel-bottom mt-3">
<div class="hotel-price">
<span class="hotel-price-amount">
{{ number_format($favorite->flight->price) }}
<span class="hotel-price-type">تومان</span>
</span>
</div>
<div class="hotel-text-btn">
<a href="{{ route('flights.show', $favorite->flight->id) }}">
نمایش جزئیات <i class="fas fa-arrow-left"></i>
</a>
</div>
</div>
</div>
</div>
</div>
@endif
@empty
<div class="col-12 text-center py-5">
<i class="far fa-heart-broken fa-3x text-muted mb-3"></i>
<h5>هنوز موردی به علاقه‌مندی‌هایتان اضافه نکرده‌اید.</h5>
<p class="text-muted mb-0">
تورها و پروازهای مورد علاقه خود را ذخیره کنید تا در هر زمان به راحتی به آن‌ها دسترسی داشته باشید.
</p>
</div>
@endforelse
</div>
@if (method_exists($favorites, 'hasPages') && $favorites->hasPages())
<div class="pagination-area my-4">
<div class="d-flex justify-content-center">
{{ $favorites->links() }}
</div>
</div>
@endif
</div>
</div>
</div>
@endsection
@@ -4,7 +4,25 @@
<div class="activity-img">
<img src="{{ $flight->file?->url ?? asset('assets/img/activity/01.jpg') }}"
alt="{{ $flight->flight_number }}">
<a href="#" class="add-wishlist"><i class="far fa-heart"></i></a>
@auth
<form action="{{ route('accounts.favorites.flight.toggle', ['flight' => $flight->id]) }}"
method="POST"
class="favorite-form">
@csrf
<button type="submit"
class="add-wishlist border-0"
title="افزودن به علاقه‌مندی‌ها">
<i class="far fa-heart"></i>
</button>
</form>
@else
<a href="{{ route('auth.login.index') }}"
class="add-wishlist"
title="برای افزودن به علاقه‌مندی‌ها وارد شوید">
<i class="far fa-heart"></i>
</a>
@endauth
</div>
<div class="activity-content">
<h4 class="activity-title">
+40 -23
View File
@@ -1,30 +1,47 @@
<div class="col-md-6 col-lg-3">
<div class="activity-item wow fadeInUp" data-wow-duration="1s" data-wow-delay=".25s">
<div class="activity-img">
<img src="{{ $tour?->file_id ?? asset('assets/img/activity/01.jpg') }}"
alt="{{ $tour->title }}">
@auth
<form action="{{ route('accounts.favorites.tour.toggle', ['tour' => $tour->id]) }}"
method="POST"
class="favorite-form">
@csrf
<div class="col-md-6 col-lg-3">
<div class="activity-item wow fadeInUp" data-wow-duration="1s" data-wow-delay=".25s">
<div class="activity-img">
<img src="{{ $tour?->file_id ?? asset('assets/img/activity/01.jpg') }}"
alt="{{ $tour->title }}">
<a href="#" class="add-wishlist"><i class="far fa-heart"></i></a>
</div>
<div class="activity-content">
<h4 class="activity-title">
<a href="#">{{ $tour->title }}</a>
</h4>
<button type="submit"
class="add-wishlist border-0"
title="افزودن به علاقه‌مندی‌ها">
<i class="far fa-heart"></i>
</button>
</form>
@else
<a href="{{ route('auth.login.index') }}"
class="add-wishlist"
title="برای افزودن به علاقه‌مندی‌ها وارد شوید">
<i class="far fa-heart"></i>
</a>
@endauth
</div>
<div class="activity-content">
<h4 class="activity-title">
<a href="#">{{ $tour->title }}</a>
</h4>
<p><i class="far fa-location-dot"></i>
{{ $tour->originCity?->name ?? 'مبدأ' }}
<i class="fas fa-arrow-right-long"></i>
{{ $tour->city?->name ?? 'مقصد' }}
</p>
<div class="activity-bottom">
<div class="activity-price">
<span class="activity-price-amount">{{ number_format(optional($tour->tourDepartures->first())->price ?? 0) }} تومان</span>
</div>
<div class="activity-text-btn">
<a href="{{route('tours.show' , $tour->id)}}">نمایش جزئیات <i class="fas fa-arrow-left"></i></a>
</div>
<p><i class="far fa-location-dot"></i>
{{ $tour->originCity->name ?? 'مبدأ' }}
<i class="fas fa-arrow-right-long"></i>
{{ $tour->city?->name ?? 'مقصد' }}
</p>
<div class="activity-bottom">
<div class="activity-price">
<span class="activity-price-amount">{{ number_format(optional($tour->tourDepartures->first())->price ?? 0) }} تومان</span>
</div>
<div class="activity-text-btn">
<a href="{{route('tours.show' , $tour->id)}}">نمایش جزئیات <i class="fas fa-arrow-left"></i></a>
</div>
</div>
</div>
</div>
</div>
+1
View File
@@ -1,6 +1,7 @@
<?php
use App\Http\Controllers\Account\EditProfileController;
use App\Http\Controllers\Account\FavoriteController;
use App\Http\Controllers\Account\ProfileController;
use App\Http\Controllers\BookingController;
use App\Http\Controllers\FlightController;