Laravel 8预订系统取消预订功能实现求助
预订系统取消功能实现问题
需求说明
- 仅当
min_before_cancel不等于0时,取消按钮才可用(否则预订不可退款) - 当前时间距预订
start_date的天数小于min_before_cancel时,取消按钮才显示(反之隐藏)
当前代码存在的问题
视图代码错误
原视图代码逻辑和语法都有问题:
@if ($service =booking->service->time_before_cancel != 0 && \Carbon\Carbon::now() >$booking->start_date->subDays($service->min_before_cancel)) <button class="btn btn-danger">Cancel</button> @endif
问题点:
- 变量赋值错误:把
time_before_cancel的值直接赋给$service,没拿到服务模型 - 逻辑判断反了:需求是当前时间早于
start_date - min_before_cancel才显示按钮,这里用了> - 污染原始数据:
subDays()会直接修改$booking->start_date的Carbon实例值
控制器方法问题
cancel方法
public function cancel($code) { // Retrieve the booking from the database $booking = Booking::find($code); // Update the booking status $booking->status = 'cancelled'; $booking->save(); // Return a success message to the user return redirect()->back()->with('success', 'Your booking has been canceled.'); }
问题点:
- 查询逻辑错误:
find($code)是按主键查,但路由参数是booking_code,会找不到数据 - 无权限校验:没判断当前用户是否能操作这个订单
- 无后端校验:前端条件可以被绕过,后端没再次验证取消时间
- 无异常处理:找不到订单时会直接报错
showCancelOption方法
public function showCancelOption($bookingCode) { // Retrieve the booking from the database $booking = Booking::where('booking_code', $bookingCode)->first(); // Retrieve the service associated with the booking $service = $booking->service; // Calculate the difference in days between the start date and today $startDate = new Carbon($booking->start_date); $today = Carbon::now(); $diffInDays = $startDate->diffInDays($today); }
问题点:
- 无返回值:没把计算好的结果传给视图
- 无权限校验:随便输入一个booking_code就能访问
- 无空值处理:找不到订单时会报错
路由与视图文件问题
- 按钮没包裹在表单里:POST路由无法触发请求
cancel.blade.php没被合理使用,逻辑混乱
模型关联问题
要确认Booking模型是否正确关联了Boat(即$booking->service能正常拿到服务数据)
修复方案
1. 修复视图代码
替换原视图代码,解决语法、逻辑问题,同时支持POST请求:
@php $service = $booking->service; // 复制start_date实例,避免修改原始数据 $cancelDeadline = $booking->start_date->copy()->subDays($service->min_before_cancel); // 整合所有取消条件:可退款、未到截止时间、订单未取消 $canCancel = $service->min_before_cancel != 0 && \Carbon\Carbon::now()->lt($cancelDeadline) && $booking->status != 'cancelled'; @endphp @if($canCancel) <form action="{{ route('booking.cancel', $booking->booking_code) }}" method="POST" onsubmit="return confirm('确定要取消预订吗?')"> @csrf <button type="submit" class="btn btn-danger">取消预订</button> </form> @endif
2. 修复控制器cancel方法
增加校验逻辑,避免非法操作:
use Illuminate\Support\Facades\Auth; public function cancel($code) { // 按booking_code查找订单,找不到直接返回404 $booking = Booking::where('booking_code', $code)->firstOrFail(); // 校验当前用户是否是订单所有者(根据你的用户关联逻辑调整) if($booking->user_id != Auth::id()) { return redirect()->back()->with('error', '无权限取消该预订'); } // 校验订单状态 if($booking->status == 'cancelled') { return redirect()->back()->with('error', '该订单已取消'); } // 后端再次校验取消时间,防止前端绕过 $service = $booking->service; $cancelDeadline = $booking->start_date->copy()->subDays($service->min_before_cancel); if($service->min_before_cancel == 0 || \Carbon\Carbon::now()->gte($cancelDeadline)) { return redirect()->back()->with('error', '已过取消时限,无法取消'); } // 更新订单状态 $booking->status = 'cancelled'; $booking->save(); // 如果需要退款,这里可以添加退款逻辑 // ... return redirect()->back()->with('success', '预订已成功取消'); }
3. 调整showCancelOption方法(如果需要单独页面)
如果要单独做取消确认页面,修改方法如下:
use Illuminate\Support\Facades\Auth; public function showCancelOption($bookingCode) { $booking = Booking::where('booking_code', $bookingCode)->firstOrFail(); // 权限校验 if($booking->user_id != Auth::id()) { abort(403, '无权限访问'); } $service = $booking->service; $cancelDeadline = $booking->start_date->copy()->subDays($service->min_before_cancel); $canCancel = $service->min_before_cancel != 0 && \Carbon\Carbon::now()->lt($cancelDeadline) && $booking->status != 'cancelled'; return view('booking.cancel', compact('booking', 'canCancel')); }
同时添加GET路由:
Route::get('/booking/{bookingCode}/cancel-option', 'BookingController@showCancelOption')->name('booking.cancel.option');
4. 确认模型关联
确保Booking模型正确关联Boat:
class Booking extends Model { // ... // 根据你的实际关联字段调整,这里假设用service_id关联Boat public function service() { return $this->belongsTo(Boat::class, 'service_id'); } // 用户关联(如果有) public function user() { return $this->belongsTo(User::class); } }
5. 整理视图文件
如果使用单独取消页面,cancel.blade.php可以这样写:
@extends('layouts.app') @section('content') <div class="container"> <h1>取消预订</h1> @if($canCancel) <form action="{{ route('booking.cancel', $booking->booking_code) }}" method="POST" onsubmit="return confirm('确定要取消预订吗?')"> @csrf <button type="submit" class="btn btn-danger">确认取消预订</button> </form> @else <p>无法取消该预订,已过取消时限或订单不可退款</p> @endif </div> @endsection
如果不需要单独页面,直接删除cancel.blade.php即可
内容的提问来源于stack exchange,提问作者Yassin
相关产品推荐
相关产品推荐

