DEV Community

Cover image for Validating Multiple Forms on a Page in Laravel 8
Zack Webster
Zack Webster

Posted on

Validating Multiple Forms on a Page in Laravel 8

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

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

// 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');
    }
}
Enter fullscreen mode Exit fullscreen mode

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

// 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);
        });
    }
}

Enter fullscreen mode Exit fullscreen mode

Here's how my InputComponent view looks like:

<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>
Enter fullscreen mode Exit fullscreen mode

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

Latest comments (2)

Collapse
 
azzumed profile image
azzumed

What about validateWithBag method in controller?

Collapse
 
xuchunyang profile image
徐春阳

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.

I beilieve validateWithBag doesn't solve this old('username') issue.