Validating Multiple Forms on a Page in Laravel 8
Tags: laravel, php, webdev
Published: 2021-02-09 16:45:03

In this post, I'd like to explain my process of handling & validating multiple forms that can have same-named fields. For example, you can have two side by side forms, say, login and signup on the same page. And you are likely to have matching fields like username, password, and such. If you use something like `old('username')` as the value, the respective fields of both forms would be populated even if you submitted just one.

#The Product
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/yr98zcpxbxf9v9s1tsdh.gif)

Disclaimer: This is experimental at the moment, I'm still refining my procedure and looking into other ideas. This is a journal of what I have come up with so far.

Alright, let's get down to it.

#The Flow
1. I add a hidden field called `_name` to my forms. Which holds an identifier for the form e.g `login`, `signup`, etc.
2. On the controller side there is no change, yet. The validation logic stays the same.
3. Before utilizing the old value of, say, username field. I check if the old value of `_name` is the same as defined earlier.

# The Setup

As you might guess this adds some repetition to the forms and I like to keep things DRY. For this, we do have components to the rescue. Note, how flexible you make them is up to you.

```php
<?PHP

// app/View/Components/Input.php

namespace App\View\Components;

use Illuminate\View\Component;

class Input extends Component
{
    public $name;
    public $id;
    public $type;
    public $label;
    public $form;

    /**
     * Create a new component instance.
     *
     * @return void
     */
    public function __construct($name= null, $id = null, $type=null, $label='text', $form = null)
    {
        //
        $this->name= $name;
        $this->id= $id ? $id : ($form && $name ? $form . '-' . $name : null);
        $this->type= $type;
        $this->label= $label;
        $this->form= $form;
    }

    /**
     * Get the view / contents that represent the component.
     *
     * @return \Illuminate\Contracts\View\View|string
     */
    public function render()
    {
        return view('components.input');
    }
}
```

Wouldn't it be good to have some helpers to perform the checks? For this, I set up custom blade directives that handle checking the conditionals.
```php
<?php

// app/Providers/AppServiceProvider.php

namespace App\Providers;

use Illuminate\Support\ViewErrorBag;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Blade::if('from', function ($form = null) {
            return old('_name') === $form;
        });

        Blade::if('invalid', function ($name, $form = null) {
            $errors = session()->get('errors', app(ViewErrorBag::class));
            return old('_name') === $form && $errors->has($name);
        });
    }
}

```

Here's how my InputComponent view looks like:
```php
<div class="mb-3">
    @if($label)
    <label for="{{ $id }}" class="form-label">{{ $label }}</label>
    @endif
    <input type="{{ $type }}" class="form-control @invalid($name, $form) is-invalid @endinvalid" id="{{ $id }}"
        name="{{ $name }}" value="@from($form){{ old($name) }}@endfrom($form)" />
    @invalid($name, $form)
    <div class="invalid-feedback">
        {{ $errors->first($name) }}
    </div>
    @endinvalid
</div>
```

I also utilize a Form component to handle adding the hidden input field as well as automate handling the method for it.

#Summary
This is just one of perhaps many more solutions out there. This works for me at the moment and I will still continue to explore more.

If you have any suggestions feel free to discuss them down below. Thank you for reading.

Oh and by the way, you can check out the project on my Github: https://github.com/zaxwebs/ex-l8-multi-forms-2