How to submit checkbox in Laravel

Problem

Let’s say you have model with fields:

 Schema::create('projects', function (Blueprint $table) {
            $table->id();
            $table->boolean('is_published')->default(false)
            $table->timestamps();
        });

Create checkbox form, suitable for create and edit, like this:

<form action="{{ route('projects.store') }}" method="POST">
    @csrf
        <input class="form-check-input" type="checkbox" id="is_published"
         name="is_published"
         @checked(old('is_published', $project->is_published ?? ''))>
        <label class="form-check-label" for="is_published">
        Is published:
        </label>
    <button class="btn btn-primary" type="submit">Submit</button>
</form>

@checked in here:

@checked(old('is_published', $project->is_published ?? ''))

is a shorthand to make the checkbox checked or unchecked when it renders. More info in the docs

Problem is, in form submission checkbox doesn’t send anything if it’s unchecked. It only sends value, default value is on, in payload if it’s checked.

Problem is because if you have validation 'is_published' => 'boolean', that will give error is published must be true or false after you submit the form.

Solution

One way to solve this is

  1. Create ProjectRequest:
php artisan make:request ProjectRequest
  1. In generated class, in method rules add validation 'is_published' => 'boolean' and function looks now like this:
  * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
     */
    public function rules(): array
    {
        return [
            'name' => 'required|string|max:255',
            'github_repo_url' => 'nullable|url',
            'website_url' => 'nullable|url',
            'is_published' => 'boolean',
        ];
    }
  1. If you don’t want authorization return true in method authorize:
 /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return true;
    }
  1. Create a new method prepareForValidation. Name is important because this overriding parent FormRequest class. In this function you can do some logic with submitted form data before it is passed to rules method.

In this method check if form submit request contains is_published key. If it does it must be true, because as is_published is not in form submit if it’s not checked.

 protected function prepareForValidation()
    {
        $this->merge([
            'is_published' => $this->has('is_published') ? true : false
        ]);
    }
  1. Now you can pass ProjectRequest to the controller action:
   /**
     * Store a newly created resource in storage.
     */
    public function store(ProjectRequest $request)
    {
        Project::create($request->validated());
        return redirect()->route('projects.index')->with('success', 'Project created.');
    }

Final word

It works on my computer.