You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel手术剧场时段防重复预约验证实现问询

Prevent Duplicate Theatre Bookings in Laravel

Hey there! As a fellow Laravel developer, let's walk through how to add that duplicate booking validation you need. Since you're new to the framework, I'll break this down into simple, actionable steps so you can follow along easily.

First, let's add a safety net at the database level to prevent duplicates even if our code logic has gaps. This ensures no conflicting bookings can slip through, no matter what.

If you already have the booking_forms table, create a new migration to add a composite unique index:

php artisan make:migration add_unique_constraint_to_booking_forms

Open the new migration file and update the up method:

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up()
    {
        Schema::table('booking_forms', function (Blueprint $table) {
            // Adjust fields based on your actual slot definition
            // If using fixed slots (e.g., morning/afternoon), add the slot field here
            // For time ranges, we'll handle overlap logic in code, but this baseline prevents exact duplicates
            $table->unique(['date', 'TheatreRoomID']);
        });
    }

    public function down()
    {
        Schema::table('booking_forms', function (Blueprint $table) {
            $table->dropUnique(['date', 'TheatreRoomID']);
        });
    }
};

Run the migration to apply the constraint:

php artisan migrate

2. Add Validation Logic (With Custom Rule for Time Overlaps)

If your bookings use custom time ranges (not fixed slots), we'll need a custom validation rule to check for overlapping bookings. If you use fixed slots (like "Morning" or "Afternoon"), skip to the simplified rule below.

Create a Custom Validation Rule

Run this command to generate a new rule:

php artisan make:rule UniqueTheatreSlot

Open app/Rules/UniqueTheatreSlot.php and update it with this logic:

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;
use App\Models\BookingForm;

class UniqueTheatreSlot implements Rule
{
    protected $theatreId;
    protected $date;
    protected $startTime;
    protected $endTime;
    protected $bookingId; // For edit scenarios to exclude the current booking

    public function __construct($theatreId, $date, $startTime, $endTime, $bookingId = null)
    {
        $this->theatreId = $theatreId;
        $this->date = $date;
        $this->startTime = $startTime;
        $this->endTime = $endTime;
        $this->bookingId = $bookingId;
    }

    public function passes($attribute, $value)
    {
        $query = BookingForm::where('TheatreRoomID', $this->theatreId)
            ->where('date', $this->date)
            // Check for overlapping time ranges:
            // Current booking starts before an existing booking ends,
            // and current booking ends after an existing booking starts
            ->where(function ($query) {
                $query->where('start_time', '<', $this->endTime)
                    ->where('end_time', '>', $this->startTime);
            });

        // Exclude the current booking if we're editing
        if ($this->bookingId) {
            $query->where('id', '!=', $this->bookingId);
        }

        // Return true if no overlapping bookings exist
        return !$query->exists();
    }

    public function message()
    {
        return 'This theatre slot is already booked for the selected date and time range.';
    }
}

Use the Rule in Your Controller

In your BookingFormController's store method (and update method if you have one), add the validation:

For Creating Bookings:

public function store(Request $request)
{
    $validated = $request->validate([
        'date' => 'required|date',
        'TheatreRoomID' => 'required|exists:theatre_rooms,id', // Ensure the theatre exists in your database
        'start_time' => 'required|date_format:H:i',
        'end_time' => [
            'required',
            'date_format:H:i',
            'after:start_time', // Ensure end time is after start time
            new \App\Rules\UniqueTheatreSlot(
                $request->TheatreRoomID,
                $request->date,
                $request->start_time,
                $request->end_time
            )
        ]
        // Add other fields from your form here
    ]);

    BookingForm::create($validated);

    return redirect()->route('bookings.index')->with('success', 'Booking created successfully!');
}

For Editing Bookings:

public function update(Request $request, BookingForm $booking)
{
    $validated = $request->validate([
        'date' => 'required|date',
        'TheatreRoomID' => 'required|exists:theatre_rooms,id',
        'start_time' => 'required|date_format:H:i',
        'end_time' => [
            'required',
            'date_format:H:i',
            'after:start_time',
            new \App\Rules\UniqueTheatreSlot(
                $request->TheatreRoomID,
                $request->date,
                $request->start_time,
                $request->end_time,
                $booking->id // Pass the current booking ID to exclude it from the check
            )
        ]
        // Add other fields here
    ]);

    $booking->update($validated);

    return redirect()->route('bookings.index')->with('success', 'Booking updated successfully!');
}

Simplified Rule for Fixed Slots

If you use fixed slots (e.g., a slot field with values like "morning", "afternoon"), you don't need a custom rule. Use Laravel's built-in unique validation with additional conditions:

$validated = $request->validate([
    'date' => 'required|date',
    'TheatreRoomID' => 'required|exists:theatre_rooms,id',
    'slot' => [
        'required',
        'unique:booking_forms,slot,NULL,id,TheatreRoomID,'.$request->TheatreRoomID.',date,'.$request->date
    ]
]);

3. Show Error Messages in Your View

Make sure your Create.blade.php view displays validation errors. Add this somewhere near the top of your form:

@if ($errors->any())
    <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

And for each input, add error styling if applicable:

<div class="form-group">
    <label for="date">Date</label>
    <input type="date" name="date" id="date" class="form-control @error('date') is-invalid @enderror" value="{{ old('date') }}">
    @error('date')
        <div class="invalid-feedback">{{ $message }}</div>
    @enderror
</div>

Final Tips

  • Test Edge Cases: Make sure to test scenarios like back-to-back bookings (e.g., one ends at 10:00, another starts at 10:00) and adjust the overlap logic in the custom rule if needed based on your business requirements.
  • Handle Database Exceptions: Even with the unique constraint, add a try/catch block around the create/update methods to catch any unexpected errors and display a user-friendly message.

内容的提问来源于stack exchange,提问作者Andrew Glenny

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 10:57:49