I tried to solve multiple clicking on the submit button by using ReCaptcha V3. It kind of works as the database insert only run once in the controller and the following times it just hit the validation returning an error.
The issue is that the controller method is not entirely run.
For example if I click multiple times on a submit button on an e-commerce page, in the CheckoutController
, the checkout
method doesn't run completely.
class CheckOutController extends Controller {
public function checkout(Request $request) {
// Some checkout Logic (insert into database)
$this->validate($request, [
'recaptcha' => ['required', new \App\Rules\Recaptcha]
]);
if (Cart::content()->count() > 0) {
foreach (Cart::content() as $cartItem) {
$insert = new \App\Transaction;
$insert->product_id = $cartItem->id;
$insert->receipt_id = $cartItem->receipt_id;
$insert->quantity = $cartItem->qty;
$insert->price= $cartItem->price;
$insert->save();
}
Cart::destroy(); //last part of checkout logic
return view('finishCheckout');
}
return abort(404);
}
}
Because I'm working locally the speed is faster so the request is iterating through my code inserting the data into my database.
Sometimes the request hits the Cart:: destroy();
part of the logic, sometimes it doesn't. But I suspect that on a production environment the code might stop elsewhere before that part.
And it never hits the return view('finishCheckout');
. It just retrying the same method and fails the validation, returning me back to the checkout page with the validation error.
Is there anything I can do to ensure that either all the method is run or stop it from running completely?
Edit: Note that this only happens when I click the submit buttons multiple times! If I would click it only once, the method runs correctly.
Before you delete use \Cart
You can use Cart::delete() or Cart::where(....)->delete()
A better solution would be to prevent the double clicking client side. make a javascript function that onSubmit
disables the submit button.
This way, the user will receive the response of the first call he made, not the last one (in case of double submit)
BTW, you can simulate a slow server. just add a sleep(3)
at the start of the controller method.
My theory is that because you are clicking the button multiple times, on one of the requests it iterates through the cart content and then reaches the Cart::destroy()
method, but then on other requests because it has already been it, it is failing the conditional and jumping straight to the abort(404)
function.
If you are making the request with Ajax, it doesn't surprise me. With asynchronous HTTP requests if you click the button again it will create a new request and allow the previous to continue, rather than cancelling it. It should be your front end that when the button is clicked, while the request is in progress it disables the button so further requests cannot be made.