Skip to content
Advertisement

How to validate at least one checkbox is checked in a loop in Laravel?

I’m trying to validate my checkbox from a loop, where at least one is checked and displays an error/alert or disable the submit button instead if there is no checkbox checked. I tried putting a required method inside my checkbox, but the checkbox is in a loop that’s why it requires all to be checked. That’s why I tried the script code below which I found on the internet but that doesn’t seem to work for me.

Below is my code in blade form

      <form id="contact" action="{{url('/reservation')}}" method="post" enctype="multipart/form-data">
      @csrf
<div class="row col-12">
        @foreach($data as $data)
                <div class="row sm-6 ml-4 mb-1" class="no-gutters" style="height:25px; width: auto;">
                    <p class='text-dark mr-2'><input type="checkbox" name="prod_name[]" value="{{$data->title}}" class="products product{{$data}}" onClick="checkTest()"/> {{$data->title}}</p>
                    <p class='text-dark'>Qty:</p><input style="width:80px; height:25px;" type="number" name="prod_qty[{{$data->title}}]" min="1" value="1" class="form-control ml-2">
                    <input type="hidden" name="product_fee[{{$data->title}}]" value="{{$data->price}}">
                    <input type="hidden" name="prod_id[{{$data->title}}]" value="{{$data->id}}">
            </div>
          @endforeach
        </div>
                <div class=" col-lg-12 mt-5">
                    <fieldset>
                    <button name="submit" type="submit" id="form-submit"  class="main-button-icon">Make A Reservation</button>
                    </fieldset>
                </div>
        </div>
</form>
<script>
    var minimumonechecked;
    var checkboxes = $('.products').lenght;
    function checkTest(xyz){
        minimumonechecked = false;
        for(i=0;i<checkboxes;i++){
            if($('product' + i).is(':checked')){
                minimumonechecked = true;
            }
        }
        console.log(minimumonechecked)

    };
</script>

This is also the code in my controller, other data such as names are also part of the form but I cut it out by focusing on the checkbox

 public function reservation(Request $request)
    {
        if(Auth::id()){
            $user_id=Auth::id();
            $products = '';
            $reserved_qty=0;
            $product_fee=0;
            $prod_id=0;
            $checked_array = $request->input('prod_name', []);
            $quantities = $request->input('prod_qty', []);
            $fees = $request->input('product_fee', []);
            $productid = $request->input('prod_id', []);
            foreach($checked_array as $value){
                $data = new reservation;
                $data->user_id=$user_id;
                $data->name=$request->name;
                $data->email=$request->email;
                $data->phone=$request->phone;
                $data->address=$request->address;
                $data->date=$request->date;
                $data->time=$request->time;
                $data->status="pending";

                $data->productz=$request->products="{$value}";
                $data->reserved_qty=$request->$reserved_qty="{$quantities[$value]}";
                $data->product_fee=$request->$product_fee=$fees[$value]*$quantities[$value];
                $data->prod_id=$request->$prod_id=$productid[$value];



                $data->save();

        }
                return redirect()->back();
        }else{
            return redirect('/login');
        }

Advertisement

Answer

There are a many ways to achieve your desired result, how I would approach it is as follows (note this is not exhaustive, it’s a functional example).

On the client, use JavaScript to select all your product checkboxes, iterate over any found and attach event handlers to each of them which listen for the changed event (i.e. checked and unchecked). When the state of a checkbox is checked, add it to a Set so that we can track how many are selected. Based on the number of checked checkboxes, enable/disable form submission.

That theory out of the way, some code:

HTML:

<script src="https://cdn.tailwindcss.com"></script>
<div class="flex flex-col place-content-center items-center h-screen space-y-4">
  <div class="flex align-items-center space-x-2">
    <h1 class="underline font-bold decoration-orange-500 decoration-2">
      <a href="https://stackoverflow.com/questions/73834200/how-to-validate-at-least-one-checkbox-is-checked-in-a-loop-in-laravel">
        How to validate at least one checkbox is checked in a loop in Laravel?
      </a>
    </h1>
  </div>
  <div class="flex align-items-center space-x-2">
    <input type="checkbox" class="products" name="products[]" id="product-a" />
    <label for="product-a" class="text-sm">Product A</label>
  </div>
  <div class="flex align-items-center space-x-2">
    <input type="checkbox" class="products" name="products[]" id="product-b" />
    <label for="product-b" class="text-sm">Product B</label>
  </div>
  <div class="flex align-items-center space-x-2">
    <input type="checkbox" class="products" name="products[]" id="product-c" />
    <label for="product-c" class="text-sm">Product C</label>
  </div>
  <div class="flex align-items-center space-x-2">
    <button class="px-4 py-2 rounded bg-orange-600 hover:bg-orange-700 text-white text-sm" id="form-submit">Submit</button>
  </div>
</div>

JavaScript:

window.addEventListener('DOMContentLoaded', function () {
  
  // get all the product checkbox elements
  var productCheckboxes = document.querySelectorAll('.products');
  
  // holder for the checked products
  var checkedProducts = new Set();
  
  // pointer to the form submit button
  var formSubmitButton = document.querySelector('#form-submit');
  
  // attach change event handlers to each of the checkboxes
  productCheckboxes.forEach((checkbox) => {
    checkbox.addEventListener('change', function (e) {
      e.target.checked 
        ? checkedProducts.add(e.target.id) 
        : checkedProducts.delete(e.target.id);
    });
  });
  
  // 
  formSubmitButton.addEventListener('click', function (e) {
    handleClientSideValidation();
  });
  
  // do some client side validation (do not rely on this alone!)
  function handleClientSideValidation() {
    if (checkedProducts.size == 0) {
      alert('Ugh-oh, something is foobar Captain. You need to select at least 1 product.');
      return;
    }
    alert('Validating ...');
  }
})

Example CodePen

Now lets apply the above to a Laravel example using @foreach and Eloquent models. I am going to assume you already have a Product model, migration and some data seeded.

Form HTML:

<form action="{{ route('reservation') }}" method="POST" id="products-form">
    <div class="flex flex-col place-content-center items-center h-screen space-y-4">
        @csrf

        <div class="flex align-items-center space-x-2">
            <h1 class="underline font-bold decoration-orange-500 decoration-2">
            <a href="https://stackoverflow.com/questions/73834200/how-to-validate-at-least-one-checkbox-is-checked-in-a-loop-in-laravel">
                How to validate at least one checkbox is checked in a loop in Laravel?
            </a>
            </h1>
        </div>

        @if ($errors->any())
            <div class="px-4 py-2 bg-red-500 text-white text-sm rounded-sm">
                <ul>
                    @foreach ($errors->all() as $error)
                        <li>{{ $error }}</li>
                    @endforeach
                </ul>
            </div>
        @endif

        @foreach($products as $product)
        <div class="flex align-items-center space-x-2">
            <input type="checkbox" class="products" name="products[]" id="product-{{ $product->id }}" value="{{ $product->id }}" />
            <label for="product-{{ $product->id }}" class="text-sm">{{ $product->title }}</label>
        </div>
        @endforeach

        <div class="flex align-items-center space-x-2">
            <button type="submit" class="px-4 py-2 rounded bg-orange-600 hover:bg-orange-700 text-white text-sm" id="form-submit">Submit</button>
        </div>
    </div>
</form>

As we all know you shouldn’t rely soley upon client side validation, so you’ll want to do some server side validation too. Once you’ve validated your request data, you can do whatever you want with it (I just get the products from the database in this example). I have put the code directly in a Route within web.php as I am lazy, you’d obviously use it in your ProductController function.

routes/web.php:

Route::post('/', function (Request $request) {
    // perform some validation
    // $validate will contain all the data that passed validation
    $validated = $request->validate([
        // products must be present in the request AND an array
        'products' => ['required', 'array'],
        // each of the products in the request, must have an ID present in the database
        'products.*' => ['exists:products,id']
    ]);

    // if we get here, validation was successfull

    // do whatever you want with the $validated data
    // I am just getting the Products as an example
    $products = AppModelsProduct::whereIn('id', $validated['products'])->get();

    // dump the Products
    dd($products->all());
})->name('reservation');

Example PHP Sandbox

Quite a lot to taken in but hopefully that makes sense.

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement