Get Appointment

Blog Single

Making Google CAPTCHA Dynamic in Laravel

  • Vfix Technology
  • 24 Dec 2023
  • Laravel
  • 345 Views

 Introduction 

CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart) is a common security feature on websites. It is used to differentiate between human users and bots, helping to protect your web applications from automated attacks and spam. Laravel, a popular PHP framework, provides a straightforward way to integrate CAPTCHA into your applications. In this blog, we'll discuss how to make CAPTCHA dynamic in Laravel, which enhances security and user experience.

If you don't know how to use captcha in laravel please check our previous blog How to Add Google No Captcha into PHP Laravel

Step 1: Create a Setting Controller, Model and  Migration

php artisan make:model Setting -mcr

By this command you can create model, migration, controller and resources

Step 2: Add fields in migration

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('settings', function (Blueprint $table) {
            $table->id();
            $table->json('captcha')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('settings');
    }
};

 Migrate table

php artisan migrate

Step 3: Setup model

Add Following code in the model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Setting extends Model
{
    use HasFactory;

    protected $fillable = [
        'captcha',
    ];

    protected $casts = [
        'captcha' => 'array',
    ];
}

Here $casts is used because we are inserting the data of the captcha in the json format if we use $casts in the model then it automatically encode and decode the json data while inserting and featching data if we will not use this then we have to write encode and decode manually to prevent this we use $casts.

Step 4: Set up Controller code 

App\Http\Controllers\SettingController

<?php

namespace App\Http\Controllers;

use App\Models\Setting;
use Illuminate\Http\Request;
use File;

class SettingController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        $setting = Setting::first();

        return view('backend.setting.index',compact('setting'));
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     */
    public function show(Setting $setting)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     */
    public function edit(Setting $setting)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, Setting $setting)
    {
        $data = $request->validate([
            'captcha.*' => 'nullable|string',
        ]);


        $setting->update($data);

        return back()->with('success','Data Updated Successfully');
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(Setting $setting)
    {
        //
    }
}

Here we are updating the data where id is 1 means the first occurence in the settings table

Step 5: Create a blade

@extends('adminlte::page')

@section('header', 'Setting')

@section('content_header')

@stop

@section('content')
<section class="content">
    <div class="container-fluid">
        <div class="row">
            <!-- /.col -->
            <div class="col-md-9">
                <div class="card">
                    <form action="{{ route('setting.update', $setting->id) }}" method="post"
                        enctype="multipart/form-data">
                        @csrf
                        @method('PATCH')
                        <div class="card-body">
                            <h4 class="pb-3" style="font-weight: 600">Google Captcha</h4>
                            <div class="form-group row">
                                <label for="" class="col-sm-2 col-form-label">Site Key</label>
                                <div class="col-sm-10">
                                    <input type="text" name="captcha[site_key]" id="" class="form-control"
                                        placeholder="Enter Site Key" value="{{ $setting->captcha['site_key'] ?? '' }}">
                                    @error('captcha[site_key]')
                                    <span class="text-danger">{{ $message }}</span>
                                    @enderror
                                </div>
                            </div>
                            <div class="form-group row">
                                <label for="" class="col-sm-2 col-form-label">Secret Key</label>
                                <div class="col-sm-10">
                                    <input type="text" name="captcha[secret_key]" id="" class="form-control"
                                        placeholder="Enter Secret Key" value="{{ $setting->captcha['secret_key'] ?? '' }}">
                                    @error('captcha[secret_key]')
                                    <span class="text-danger">{{ $message }}</span>
                                    @enderror
                                </div>
                            </div>
                            <div class="form-group">
                                <div class="custom-control custom-switch">
                                    <input type="hidden" name="captcha[contact_form]" value="0">
                                    <!-- Default value when unchecked -->
                                    <input type="checkbox" class="custom-control-input" id="contact_form"
                                        name="captcha[contact_form]" value="1" @if ($setting->captcha['contact_form'] ==
                                    true) checked @endif>
                                    <label class="custom-control-label" for="contact_form">Disable/Enable
                                        Captcha</label>
                                    <small>&nbsp;&nbsp;disable/enable the captcha for the form on the
                                        website</small>
                                </div>
                            </div>
                        </div>
                        <!-- /.tab-content -->
                        <div class="form-group row">
                            <div class="offset-sm-2 col-sm-10">
                                <button onclick="return confirm('Are you sure you want to update profile?');"
                                    type="submit" class="btn btn-danger">Update</button>
                            </div>
                        </div>
                    </form>
                </div><!-- /.card-body -->
            </div>
            <!-- /.card -->
        </div>
        <!-- /.row -->
    </div><!-- /.container-fluid -->
</section>
@stop

Here I have taken the Site key, secret key and the Contact form captcha active or deactivate

After this you can insert the data in the table and can update the data but still it is not dynamic  to control the captcha on the website

Step 6: Get the data form the database

As you know if you have read our previous blogs of captcha we have to set the Site and Secret Key in the .env so here we are not going to pass the data in the .env file and we are going to use it directly 

Open Provider/AppServiceProvider and add the below code there

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Models\Setting;
use View;
use Config;

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

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        $settingData = Setting::whereId('1')->firstOrFail();

        Config::set('captcha.sitekey', $settingData->captcha['site_key']);
        Config::set('captcha.secret', $settingData->captcha['secret_key']);
        Config::set('custom.forms.contactform', $settingData->captcha['contact_form'];
        Config::set('custom.forms.loginform', $settingData->captcha['loginform']);

    }
}

Here we are passing the data form the table to the captcha.php file located in the config folder so here we are accessing the sitekey located in the config/captcha.php and give the $settingData->captcha['site_key'] to sitekey and $settingData->captcha['secret_key'] to secret.

Now you can use this as dynamic and can change the keys dynamically using the admin panel without going in the code 

Now to activate and dactivate the captcha in the contact form we have inserted a toggle data 1 or 0 but using toggle button you can check it in the blade code to use that simple 

1. Create a custom config

Create the custom.php file in config folder and add this code there. 

<?php

return [
    'forms' => [
        'contactform',
        'loginform'
    ],
];

Here you create your own keys which you can use for further uses.

Now use in blade 

 <form method="post" action="{{ route('contact.mail') }}">
     @csrf
     <div class="row">
         <div class="col-md-6 mb-3">
             <input type="text" name="name" class="form-control mb-0 @error('name') is-invalid @enderror"
                 value="{{ old('name') }}" placeholder="Your Name:">
             @error('name')
             <span class="text-danger">{{ $message }}</span>
             @enderror
         </div>
         <div class="col-md-6 mb-3">
             <input type="tel" value="{{ old('phone') }}" class="form-control mb-0 @error('phone') is-invalid @enderror"
                 name="phone" placeholder="Telephone:">
             @error('phone')
             <span class="text-danger">{{ $message }}</span>
             @enderror
         </div>
         <div class="col-md-12 mb-3">
             <input type="email" value="{{ old('email') }}"
                 class="form-control mb-0 @error('email') is-invalid @enderror" name="email" placeholder="Email:">
             @error('email')
             <span class="text-danger">{{ $message }}</span>
             @enderror
         </div>
         <div class="col-md-12 mb-3">
             <textarea name="message" rows="7" class="mb-0" placeholder="Message:">{{ old('message') }}</textarea>
             @error('message')
             <span class="text-danger">{{ $message }}</span>
             @enderror
         </div>

         <div class="col-md-12 mb-3">
             @if (config('custom.form.contactform') == 1)
             <div class="mb-0">
                 {!! NoCaptcha::renderJs() !!}
                 {!! NoCaptcha::display() !!}
             </div>
             @error('g-recaptcha-response')
             <span class="text-danger">{{ $message }}</span>
             @enderror
             @endif
         </div>

         <div class="col-md-12">
             <button type="submit" class="butn">Submit <i
                     class="fas fa-long-arrow-alt-right margin-10px-left"></i></button>
         </div>


Here if the $settingData->captcha['contact_form'] is 1 in the database table then the captcha will visible on the website if it is 0 will not visible on the website.

Similarly you can use this for login page

 <form method="post" action="{{ route('contact.mail') }}">
     @csrf
     <div class="row">
         <div class="col-md-12 mb-3">
             <input type="email" value="{{ old('email') }}"
                 class="form-control mb-0 @error('email') is-invalid @enderror" name="email" placeholder="Email:">
             @error('email')
             <span class="text-danger">{{ $message }}</span>
             @enderror
         </div>
         <div class="col-md-6 mb-3">
             <input type="pasword" value="" class="form-control mb-0 @error('password') is-invalid @enderror"
                 name="password" placeholder="Password">
             @error('phone')
             <span class="text-danger">{{ $message }}</span>
             @enderror
         </div>

         <div class="col-md-12 mb-3">
             @if (config('custom.form.loginform') == 1)
             <div class="mb-0">
                 {!! NoCaptcha::renderJs() !!}
                 {!! NoCaptcha::display() !!}
             </div>
             @error('g-recaptcha-response')
             <span class="text-danger">{{ $message }}</span>
             @enderror
             @endif
         </div>
         <div class="col-md-12">
             <button type="submit" class="butn">Submit <i
                     class="fas fa-long-arrow-alt-right margin-10px-left"></i></button>
         </div>
 </form>

Now let’s add the login into controller for login:

    protected function validateLogin(Request $request)
     {
         if (config('custom.forms.loginform') == 1)
         {

             $request->validate([
                 'email' => 'required',
                 'password' => 'required',
                 'g-recaptcha-response' => 'required|captcha'
             ],
             [
                 'g-recaptcha-response' => [
                     'required' => 'Please verify that you are not a robot.',
                     'captcha' => 'Captcha error! try again later or contact site admin.',
                 ]
                 ],
         );

         }else{
             $request->validate([
                 'email' => 'required',
                 'password' => 'required',
             ]);
         }
     }

And similarly you can use this for any forms and  you can use similarly this  " config('custom.form.loginform') == 1 " in controller logic if condition



+91 8447 525 204 Request Estimate