feat: implement favorites for tours and flights
This commit is contained in:
@@ -3,33 +3,101 @@
|
|||||||
namespace App\Http\Controllers\Account;
|
namespace App\Http\Controllers\Account;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Favorite;
|
||||||
|
use App\Models\Flight;
|
||||||
|
use App\Models\Tour;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class FavoriteController extends Controller
|
class FavoriteController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
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()
|
public function comments()
|
||||||
{
|
{
|
||||||
|
$title = 'کامنت های من';
|
||||||
$comments = auth()->user()
|
$comments = auth()->user()
|
||||||
->comments()
|
->comments()
|
||||||
->with([
|
->with([
|
||||||
@@ -36,8 +37,6 @@ class ProfileController extends Controller
|
|||||||
->orderByDesc('created_at')
|
->orderByDesc('created_at')
|
||||||
->paginate(10);
|
->paginate(10);
|
||||||
|
|
||||||
return view('account.comments' , compact('comments'));
|
return view('account.comments' , compact('title','comments'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,4 +37,17 @@ class Favorite extends Model
|
|||||||
'tour_id',
|
'tour_id',
|
||||||
'flight_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">
|
<div class="activity-img">
|
||||||
<img src="{{ $flight->file?->url ?? asset('assets/img/activity/01.jpg') }}"
|
<img src="{{ $flight->file?->url ?? asset('assets/img/activity/01.jpg') }}"
|
||||||
alt="{{ $flight->flight_number }}">
|
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>
|
||||||
<div class="activity-content">
|
<div class="activity-content">
|
||||||
<h4 class="activity-title">
|
<h4 class="activity-title">
|
||||||
|
|||||||
@@ -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">
|
<button type="submit"
|
||||||
<div class="activity-item wow fadeInUp" data-wow-duration="1s" data-wow-delay=".25s">
|
class="add-wishlist border-0"
|
||||||
<div class="activity-img">
|
title="افزودن به علاقهمندیها">
|
||||||
<img src="{{ $tour?->file_id ?? asset('assets/img/activity/01.jpg') }}"
|
<i class="far fa-heart"></i>
|
||||||
alt="{{ $tour->title }}">
|
</button>
|
||||||
<a href="#" class="add-wishlist"><i class="far fa-heart"></i></a>
|
</form>
|
||||||
</div>
|
@else
|
||||||
<div class="activity-content">
|
<a href="{{ route('auth.login.index') }}"
|
||||||
<h4 class="activity-title">
|
class="add-wishlist"
|
||||||
<a href="#">{{ $tour->title }}</a>
|
title="برای افزودن به علاقهمندیها وارد شوید">
|
||||||
</h4>
|
<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>
|
<p><i class="far fa-location-dot"></i>
|
||||||
{{ $tour->originCity?->name ?? 'مبدأ' }}
|
{{ $tour->originCity->name ?? 'مبدأ' }}
|
||||||
<i class="fas fa-arrow-right-long"></i>
|
<i class="fas fa-arrow-right-long"></i>
|
||||||
{{ $tour->city?->name ?? 'مقصد' }}
|
{{ $tour->city?->name ?? 'مقصد' }}
|
||||||
</p>
|
</p>
|
||||||
<div class="activity-bottom">
|
<div class="activity-bottom">
|
||||||
<div class="activity-price">
|
<div class="activity-price">
|
||||||
<span class="activity-price-amount">{{ number_format(optional($tour->tourDepartures->first())->price ?? 0) }} تومان</span>
|
<span class="activity-price-amount">{{ number_format(optional($tour->tourDepartures->first())->price ?? 0) }} تومان</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="activity-text-btn">
|
<div class="activity-text-btn">
|
||||||
<a href="{{route('tours.show' , $tour->id)}}">نمایش جزئیات <i class="fas fa-arrow-left"></i></a>
|
<a href="{{route('tours.show' , $tour->id)}}">نمایش جزئیات <i class="fas fa-arrow-left"></i></a>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Http\Controllers\Account\EditProfileController;
|
use App\Http\Controllers\Account\EditProfileController;
|
||||||
|
use App\Http\Controllers\Account\FavoriteController;
|
||||||
use App\Http\Controllers\Account\ProfileController;
|
use App\Http\Controllers\Account\ProfileController;
|
||||||
use App\Http\Controllers\BookingController;
|
use App\Http\Controllers\BookingController;
|
||||||
use App\Http\Controllers\FlightController;
|
use App\Http\Controllers\FlightController;
|
||||||
|
|||||||
Reference in New Issue
Block a user