0

I could not find an answer on my question so I hope someone can help me

I want to validate if I add a new appointment that the chosen employee has not been chosen on the day of the appointment. So I can't double-book someone on a day. I'm using laravel 5.6 and MySQL with table appointments using following rows: id, day, employee_id and resource_id

My controller is a resource controller (with the index,create,store,... functions).

So if $appointmentExists is 1 I need to throw an error and stay on the same page of the create form.

public function store(Request $request)
{
    $appointmentExist = \DB::table('appointments')
        ->where([
            ['day','=',$request->day],
            ['employee_id','=',$request->employee_id],
        ])
        ->exists();
    $request->validate([
        'day' => 'required|min:1|max:10',
        'employee_id' => 'required',
        'resource_id' => 'required',
        $appointmentExist => 'in:0',
    ]);
    $appointment = Appointment::create(['day' => $request->day, 'employee_id' => $request->employee_id, 'resource_id' => $request->resource_id]);
    return redirect('/appointments/' . $appointment->id);
}

I hope someone can help

1
  • If you ask the database whether record exists, that doesn't mean jack. Doublebooking will STILL be possible with that. What you have to do is place a unique constraint on [day, employee_id], then database definitely won't allow more than 1 record. Your next step is to insert the data. If record exists, Laravel will throw an exception. Code 23000 means duplicate record. You use that to tell your user that an appointment has been booked. Commented Mar 20, 2018 at 14:40

3 Answers 3

1

So I found the answer myself, maybe someone else can use it:

if(\DB::table('appointments')
    ->where([
        ['day','=',$request->day],
        ['employee_id','=',$request->employee_id],
    ])
    ->exists()){
        return redirect()->back()->withErrors(['This employee is already busy for that day, select another employee or another day.']);
    };

So now I respond with the error 'this employee is already busy for that day,...'. I have not found how I return the errors from $request->validate(), but I don't need that in this occasion. If you know feel free to let me know.

Sign up to request clarification or add additional context in comments.

Comments

1

Your problem is this line:

$appointmentExist => 'in:0',

That's checking that in_array($request->input($appointmentExist), [0]), but $request->input($appointmentExist) would be checking for $request->input(0) or $request->input(1), neither of which technically exist.

I would change to use Request additions:

$exists = \DB::table(...)->exists(); // Same query, just assigned to a variable
$request->request->add(["exists", $exists]);

$request->validate([
  ...,
  "exists" => "in:0"
]);

By adding the key "exists" to the request payload, you can then validate it as you would the actual data being sent in the request, and return all errors at once.

Following @N.B.'s comment, the above would only prevent double-booking for this situation; as should the validation fail, Appointment::create() would never be called, and the data would not be inserted.

With that in mind, should the validation pass unexpectedly, it's best to have a fallback, in this case a unique constraint on the combination of employee_id and day, if you truly want to prevent double-booking, and handle like so:

try {
  Appointment::create(...);
catch (\Illuminate\Database\QueryException $qex){
  \Log::error("Unable to Create Appointment: ".$qex->getMessage());

  // Handle individual codes
  if($qex->getCode() == "23000"){
    return redirect()->back()->withErrors(...);
  }
}

7 Comments

This won't prevent double booking.
@N.B. I see your comment above. When I do validation, I do $validator = \Validator::make(...); ... if($validator->passes()){ ... };; I've never actually used $request->validate(). Is there a missing step here?
Validation itself is fine, save the part with asking the database if the record exists. OP should validate basic info, such as if employee_id is present and so on. The next part, verifying whether record exists or not doesn't go into validation logic. You simply insert (and place unique constraint beforehand). If an exception is thrown, record is there, and at that point you can catch-re-throw ValidationException with message appointment exists. That way you are 100% sure that no "two users at the same time" problem bites you.
@N.B. I agree with everything you say; there's definitely better ways to handle duplicate prevention. That being said, this answer was meant as an address of the original answer by the asker on how to return all the validation messages at once, and more importantly the logic error on why $appointmentExist => 'in:0' is "invalid validation". Thanks for the feedback though.
Bear in mind that OP also wrote: So I can't double-book someone on a day.. If you include the part with preventing double booking, I'll be more than happy to upvote your answer. Reason I'm commenting is that a lot of people will google and stumble upon this answer.
|
0
$request->validate([
        'day' => 'required|min:1|max:10',
        'employee_id' => 'required',
        'resource_id' => 'required',
        $appointmentExist => 'in:0',
    ]);

This invalid code. Validator will search 1 or 0 ($appointmentExist) in request data. These keys never will include in this request.

Try use Rule class. Example:

$day = $request->day;

$request->validate([
    'day' => 'required|min:1|max:10',
    'employee_id' => [
        'required',
            Rule::unique('appointments')->where(function ($query) use ($day) {
               return $query->where('day', $day);
            })
        ],
    'resource_id' => 'required'
]);

1 Comment

This also won't prevent double booking.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.