mewebstudio / captcha

Captcha for Laravel 5/6/7/8/9/10/11
MIT License
2.46k stars 453 forks source link

validation error in ajax form #244

Open schel4ok opened 2 years ago

schel4ok commented 2 years ago

I can use captcha in a simple form, but in ajax form I always see validation error wrong captcha. I cannot understand how to fix it.

blade view

  <form method="POST" action="/modalform" method="POST" @submit.prevent="submitData()">
    @csrf

  <div class="bg-white">
    <div class="modalheader flex place-items-center text-center border-b cursor-pointer text-lg leading-6 font-medium text-gray-900">
        <h3 class="p-2 hover:bg-blue-500 hover:text-white"    
            @click="$dispatch('callback')"
            :class="callback ? 'bg-blue-500 text-white' : ''"
            >
            Перезвоните мне
        </h3>
        <h3 class="p-2 hover:bg-blue-500 hover:text-white"    
            @click="$dispatch('zamer')"
            :class="zamer ? 'bg-blue-500 text-white' : ''"
            >
            Записаться на замер
        </h3>
        <h3 class="p-2 hover:bg-blue-500 hover:text-white"    
            @click="$dispatch('eskiz')"
            :class="eskiz ? 'bg-blue-500 text-white' : ''"
            >
            Отправить эскиз
        </h3>
        <div class="p-2 place-self-stretch hover:bg-blue-500 hover:text-white" @click="closeModal()" >
            <span class="text-3xl">&times;</span>
        </div>
    </div>

    <div class="modalbody flex items-center w-full h-full p-5" 
        x-show="sent"
        x-text="message"
        x-transition:enter="transition ease-out duration-500"
        x-transition:enter-start="opacity-0 scale-90"
        x-transition:enter-end="opacity-100 scale-100"
        x-transition:leave="transition ease-in duration-200"
        x-transition:leave-start="opacity-100 "
        x-transition:leave-end="opacity-0 "
        >
    </div>

    <div class="modalbody flex items-start flex-wrap p-5" 
        x-show="!sent"
        x-transition:enter="transition ease-out duration-500"
        x-transition:enter-start="opacity-0 scale-90"
        x-transition:enter-end="opacity-100 scale-100"
        x-transition:leave="transition ease-in duration-200"
        >

      <div class="text-left w-full">

        <div class="mt-2 grid grid-cols-2 gap-x-4 gap-y-2 mb-2">

            <!-- Name --> 
            <div class="name" 
                :class="errorData.name ? 'text-red-500' : ''" 
                >
                <x-modules.label for="name" :value="__('auth.user.name')" />
                <div class="relative text-gray-400 focus-within:text-gray-800">
                    <div class="absolute flex border border-transparent left-0 top-0 h-full w-10" >
                        <x-modules.svg type="user-solid" class="flex items-center justify-center rounded-l bg-gray-100 h-full w-full px-0.5"/>
                    </div>
                    <x-modules.input id="name" class="block w-full pl-12" type="text" name="name" :value="old('name')" x-model="formData.name" placeholder="Введите имя" autofocus />
                </div>
                <span x-text="errorData.name" class="text-red-500 text-xs"> </span>
            </div>

            <!-- Phone -->
            <div class="phone" 
                :class="errorData.phone ? 'text-red-500' : ''"
                    >
                <x-modules.label for="phone" :value="__('auth.user.phone')" />
                <div class="relative text-gray-400 focus-within:text-gray-800">
                    <div class="absolute flex border border-transparent left-0 top-0 h-full w-10 ">
                        <x-modules.svg type="phone-ringing-outline" class="flex items-center justify-center rounded-l bg-gray-100 h-full w-full px-0.5"/>
                    </div>
                    <x-modules.input id="phone" class="block w-full pl-12" type="text" name="phone" :value="old('phone')" x-model="formData.phone" placeholder="Введите телефон" required autofocus />
                </div>
                <span x-text="errorData.phone" class="text-red-500 text-xs"> </span>
            </div>

            <!-- Email Address -->
            <div class="email" 
                x-show="zamer || eskiz" 
                :class="errorData.email ? 'text-red-500' : ''" 
                    >                
                <x-modules.label for="email" :value="__('email')" />
                <div class="relative text-gray-400 focus-within:text-gray-800">
                    <div class="absolute flex border border-transparent left-0 top-0 h-full w-10 ">
                        <x-modules.svg type="envelope-outline" class="flex items-center justify-center rounded-l bg-gray-100 h-full w-full px-0.5"/>
                    </div>
                    <x-modules.input id="email" class="block w-full pl-12" type="email" name="email" :value="old('email')" x-model="formData.email"  autofocus />
                </div>
                <span x-text="errorData.email" class="text-red-500 text-xs"> </span>
            </div>

            <!-- Address -->
            <div class="address" 
                x-show="zamer || eskiz" 
                :class="errorData.address ? 'text-red-500' : ''"
                    >
                <x-modules.label for="address" :value="__('auth.user.address')" />
                <div class="relative text-gray-400 focus-within:text-gray-800">
                    <div class="absolute flex border border-transparent left-0 top-0 h-full w-10 ">
                        <x-modules.svg type="facade" class="flex items-center justify-center rounded-l bg-gray-100 h-full w-full px-0.5"/>
                    </div>
                    <x-modules.input id="address" class="block w-full pl-12" type="text" name="address" :value="old('address')" x-model="formData.address" autofocus />
                </div>
                <span x-text="errorData.address" class="text-red-500 text-xs"> </span>
            </div>

            <!-- Upload field -->
            <div class="upload" x-show="eskiz">
                <label class="flex items-center justify-evenly p-2 bg-white text-gray-700 rounded-lg shadow-lg border border-gray-300 cursor-pointer hover:bg-blue-500 hover:text-white">
                    <x-modules.svg type="upload" class="w-8 h-8"/>
                    <span>Выберите файл</span>
                    <input type="file" class="hidden" multiple />
                </label>
            </div>

        </div>

        <!-- Message -->
        <div class="message">
            <x-modules.label for="message" :value="__('auth.user.message')" />
            <x-modules.textarea rows="2" id="message" class="block w-full" name="message" x-model="formData.message" placeholder="Кратко опишите ваш вопрос"/></textarea>
            <span x-text="errorData.message" class="text-red-500 text-xs"> </span>
        </div>

        <!-- captcha -->
        <div class="mt-4 captcha flex">
            <div class="w-32 mr-4 img">{!! captcha_img() !!}</div>
            <x-modules.svg type="refresh" class="w-6 h-6 mr-1 reload cursor-pointer" id="reload"/>
        </div>
        <div class="mt-4">
            <x-modules.input id="captcha" class="block mt-1 w-full" type="text" name="captcha" placeholder="Enter Captcha" x-model="formData.captcha" required />
        </div>

      </div>
    </div>

    <div class="modalfooter bg-gray-50 px-4 py-3 sm:px-6 flex justify-between ">
      <x-modules.button text="Отмена" style="black-outline" class="px-4" @click.prevent="closeModal()" />
      <x-modules.button x-text="buttonLabel" style="blue-solid" class="px-4" @click.prevent="submitData()" />
    </div>

  </div>

  </form>

<script>

    function topbar() {
        return {
            mailTooltip: false,
            instagramTooltip: false,
            openModal: false,
            callback: true,
            zamer: false,
            eskiz: false,

            formData: {
              name: '',
              phone: '',
              email: '',
              address: '',
              message: '',
              captcha: '',
              _token: '{{ csrf_token() }}'
            },

            message: '',
            responseData: [],
            errorStates: {
              name: false,
              phone: false,
              email: false,
              address: false,
              message: false,
              captcha: false
            },
            errorData: [],
            loading: false,
            sent: false,
            buttonLabel: 'Отправить',

            resetFields() {
                this.formData.name = '',
                this.formData.phone = '',
                this.formData.email = '',
                this.formData.address = '',
                this.formData.message = '',
                this.formData.captcha = ''
            },

            closeModal() {
                this.openModal = false;
                this.callback = false;
                this.zamer = false;
                this.eskiz = false;
            },

            submitData() {

                axios.post('/modalform', this.formData)
                .then( (response) => {
                    this.buttonLabel = 'Отправляем...';
                    this.loading = true;
                    console.log(response);
                    this.resetFields();
                    this.sent = true;
                    this.message = 'Сообщение успешно отправлено!';
                    this.responseData = response.data;
                })

                .then( () => {
                    this.loading = false;
                    this.sent = false;
                    this.closeModal();
                    this.buttonLabel = 'Отправить';
                    this.message = '';
                })

                .catch( (error) => {
                    console.log(error);
                    this.message = 'Ooops! Что-то пошло не так!'
                    this.errorData = error.response.data.errors;
                    this.isErrorName();
                });
            },

            isErrorName() {
                if (error.response.data.errors.name === 'undefined') {
                    this.errorStates.name = false;
                } else {
                    this.errorStates.name = true;
                }
            },

        }

    }

</script>

routes/web.php

Route::post('/modalform',    'MainController@modalform')->name('modalform');

app/Http/Controllers/MainController.php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use App\Mail\Modalform;
use App\Http\Requests\ModalformRequest;

class MainController extends Controller
{

    public function modalform(ModalformRequest $request) {

        Mail::to( config('mail.to.address') )->send(new Modalform());

        return response()->json([
            'status'            => 'success',
            'messageHeader'     => 'Ваш вопрос отправлен!', 
            'messageContent'    => 'В ближайшее время мы свяжемся с вами.',
            ]);

    }

}

app/Mail/Modalform.php

use Illuminate\Http\Request;
use App\Http\Requests\ModalformRequest;
use Illuminate\Mail\Mailable;

class Modalform extends Mailable
{

    public function build(ModalformRequest $request)
    {
        $this->from( config('mail.from.address') )
             ->view('emails.modalform')
             ->withRequest($request);
    }
}

App/Http/Requests/ModalformRequest.php

<?php

use Illuminate\Foundation\Http\FormRequest;

class ModalformRequest extends FormRequest
{
    public function rules()
    {
        return [
            'name'      => 'bail|required|string|between:2,20',        
            'phone'     => 'bail|required',
            'email'     => 'bail|email:rfc|nullable',
            'address'   => 'bail|string|max:100|nullable',
            'message'   => 'bail|string|max:500|nullable',
            'captcha'   => 'required|captcha',
        ];
    }
}
Reymoh75 commented 1 year ago

I can use captcha in a simple form, but in ajax form I always see validation error wrong captcha. I cannot understand how to fix it.

blade view

  <form method="POST" action="/modalform" method="POST" @submit.prevent="submitData()">
    @csrf

  <div class="bg-white">
    <div class="modalheader flex place-items-center text-center border-b cursor-pointer text-lg leading-6 font-medium text-gray-900">
        <h3 class="p-2 hover:bg-blue-500 hover:text-white"    
            @click="$dispatch('callback')"
            :class="callback ? 'bg-blue-500 text-white' : ''"
            >
            Перезвоните мне
        </h3>
        <h3 class="p-2 hover:bg-blue-500 hover:text-white"    
            @click="$dispatch('zamer')"
            :class="zamer ? 'bg-blue-500 text-white' : ''"
            >
            Записаться на замер
        </h3>
        <h3 class="p-2 hover:bg-blue-500 hover:text-white"    
            @click="$dispatch('eskiz')"
            :class="eskiz ? 'bg-blue-500 text-white' : ''"
            >
            Отправить эскиз
        </h3>
        <div class="p-2 place-self-stretch hover:bg-blue-500 hover:text-white" @click="closeModal()" >
            <span class="text-3xl">&times;</span>
        </div>
    </div>

    <div class="modalbody flex items-center w-full h-full p-5" 
        x-show="sent"
        x-text="message"
        x-transition:enter="transition ease-out duration-500"
        x-transition:enter-start="opacity-0 scale-90"
        x-transition:enter-end="opacity-100 scale-100"
        x-transition:leave="transition ease-in duration-200"
        x-transition:leave-start="opacity-100 "
        x-transition:leave-end="opacity-0 "
        >
    </div>

    <div class="modalbody flex items-start flex-wrap p-5" 
        x-show="!sent"
        x-transition:enter="transition ease-out duration-500"
        x-transition:enter-start="opacity-0 scale-90"
        x-transition:enter-end="opacity-100 scale-100"
        x-transition:leave="transition ease-in duration-200"
        >

      <div class="text-left w-full">

        <div class="mt-2 grid grid-cols-2 gap-x-4 gap-y-2 mb-2">

            <!-- Name --> 
            <div class="name" 
                :class="errorData.name ? 'text-red-500' : ''" 
                >
                <x-modules.label for="name" :value="__('auth.user.name')" />
                <div class="relative text-gray-400 focus-within:text-gray-800">
                    <div class="absolute flex border border-transparent left-0 top-0 h-full w-10" >
                        <x-modules.svg type="user-solid" class="flex items-center justify-center rounded-l bg-gray-100 h-full w-full px-0.5"/>
                    </div>
                    <x-modules.input id="name" class="block w-full pl-12" type="text" name="name" :value="old('name')" x-model="formData.name" placeholder="Введите имя" autofocus />
                </div>
                <span x-text="errorData.name" class="text-red-500 text-xs"> </span>
            </div>

            <!-- Phone -->
            <div class="phone" 
                :class="errorData.phone ? 'text-red-500' : ''"
                    >
                <x-modules.label for="phone" :value="__('auth.user.phone')" />
                <div class="relative text-gray-400 focus-within:text-gray-800">
                    <div class="absolute flex border border-transparent left-0 top-0 h-full w-10 ">
                        <x-modules.svg type="phone-ringing-outline" class="flex items-center justify-center rounded-l bg-gray-100 h-full w-full px-0.5"/>
                    </div>
                    <x-modules.input id="phone" class="block w-full pl-12" type="text" name="phone" :value="old('phone')" x-model="formData.phone" placeholder="Введите телефон" required autofocus />
                </div>
                <span x-text="errorData.phone" class="text-red-500 text-xs"> </span>
            </div>

            <!-- Email Address -->
            <div class="email" 
                x-show="zamer || eskiz" 
                :class="errorData.email ? 'text-red-500' : ''" 
                    >                
                <x-modules.label for="email" :value="__('email')" />
                <div class="relative text-gray-400 focus-within:text-gray-800">
                    <div class="absolute flex border border-transparent left-0 top-0 h-full w-10 ">
                        <x-modules.svg type="envelope-outline" class="flex items-center justify-center rounded-l bg-gray-100 h-full w-full px-0.5"/>
                    </div>
                    <x-modules.input id="email" class="block w-full pl-12" type="email" name="email" :value="old('email')" x-model="formData.email"  autofocus />
                </div>
                <span x-text="errorData.email" class="text-red-500 text-xs"> </span>
            </div>

            <!-- Address -->
            <div class="address" 
                x-show="zamer || eskiz" 
                :class="errorData.address ? 'text-red-500' : ''"
                    >
                <x-modules.label for="address" :value="__('auth.user.address')" />
                <div class="relative text-gray-400 focus-within:text-gray-800">
                    <div class="absolute flex border border-transparent left-0 top-0 h-full w-10 ">
                        <x-modules.svg type="facade" class="flex items-center justify-center rounded-l bg-gray-100 h-full w-full px-0.5"/>
                    </div>
                    <x-modules.input id="address" class="block w-full pl-12" type="text" name="address" :value="old('address')" x-model="formData.address" autofocus />
                </div>
                <span x-text="errorData.address" class="text-red-500 text-xs"> </span>
            </div>

            <!-- Upload field -->
            <div class="upload" x-show="eskiz">
                <label class="flex items-center justify-evenly p-2 bg-white text-gray-700 rounded-lg shadow-lg border border-gray-300 cursor-pointer hover:bg-blue-500 hover:text-white">
                    <x-modules.svg type="upload" class="w-8 h-8"/>
                    <span>Выберите файл</span>
                    <input type="file" class="hidden" multiple />
                </label>
            </div>

        </div>

        <!-- Message -->
        <div class="message">
            <x-modules.label for="message" :value="__('auth.user.message')" />
            <x-modules.textarea rows="2" id="message" class="block w-full" name="message" x-model="formData.message" placeholder="Кратко опишите ваш вопрос"/></textarea>
            <span x-text="errorData.message" class="text-red-500 text-xs"> </span>
        </div>

        <!-- captcha -->
        <div class="mt-4 captcha flex">
            <div class="w-32 mr-4 img">{!! captcha_img() !!}</div>
            <x-modules.svg type="refresh" class="w-6 h-6 mr-1 reload cursor-pointer" id="reload"/>
        </div>
        <div class="mt-4">
            <x-modules.input id="captcha" class="block mt-1 w-full" type="text" name="captcha" placeholder="Enter Captcha" x-model="formData.captcha" required />
        </div>

      </div>
    </div>

    <div class="modalfooter bg-gray-50 px-4 py-3 sm:px-6 flex justify-between ">
      <x-modules.button text="Отмена" style="black-outline" class="px-4" @click.prevent="closeModal()" />
      <x-modules.button x-text="buttonLabel" style="blue-solid" class="px-4" @click.prevent="submitData()" />
    </div>

  </div>

  </form>

<script>

    function topbar() {
        return {
            mailTooltip: false,
            instagramTooltip: false,
            openModal: false,
            callback: true,
            zamer: false,
            eskiz: false,

            formData: {
              name: '',
              phone: '',
              email: '',
              address: '',
              message: '',
              captcha: '',
              _token: '{{ csrf_token() }}'
            },

            message: '',
            responseData: [],
            errorStates: {
              name: false,
              phone: false,
              email: false,
              address: false,
              message: false,
              captcha: false
            },
            errorData: [],
            loading: false,
            sent: false,
            buttonLabel: 'Отправить',

            resetFields() {
                this.formData.name = '',
                this.formData.phone = '',
                this.formData.email = '',
                this.formData.address = '',
                this.formData.message = '',
                this.formData.captcha = ''
            },

            closeModal() {
                this.openModal = false;
                this.callback = false;
                this.zamer = false;
                this.eskiz = false;
            },

            submitData() {

                axios.post('/modalform', this.formData)
                .then( (response) => {
                    this.buttonLabel = 'Отправляем...';
                    this.loading = true;
                    console.log(response);
                    this.resetFields();
                    this.sent = true;
                    this.message = 'Сообщение успешно отправлено!';
                    this.responseData = response.data;
                })

                .then( () => {
                    this.loading = false;
                    this.sent = false;
                    this.closeModal();
                    this.buttonLabel = 'Отправить';
                    this.message = '';
                })

                .catch( (error) => {
                    console.log(error);
                    this.message = 'Ooops! Что-то пошло не так!'
                    this.errorData = error.response.data.errors;
                    this.isErrorName();
                });
            },

            isErrorName() {
                if (error.response.data.errors.name === 'undefined') {
                    this.errorStates.name = false;
                } else {
                    this.errorStates.name = true;
                }
            },

        }

    }

</script>

routes/web.php

Route::post('/modalform',    'MainController@modalform')->name('modalform');

app/Http/Controllers/MainController.php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use App\Mail\Modalform;
use App\Http\Requests\ModalformRequest;

class MainController extends Controller
{

    public function modalform(ModalformRequest $request) {

        Mail::to( config('mail.to.address') )->send(new Modalform());

        return response()->json([
            'status'            => 'success',
            'messageHeader'     => 'Ваш вопрос отправлен!', 
            'messageContent'    => 'В ближайшее время мы свяжемся с вами.',
            ]);

    }

}

app/Mail/Modalform.php

use Illuminate\Http\Request;
use App\Http\Requests\ModalformRequest;
use Illuminate\Mail\Mailable;

class Modalform extends Mailable
{

    public function build(ModalformRequest $request)
    {
        $this->from( config('mail.from.address') )
             ->view('emails.modalform')
             ->withRequest($request);
    }
}

App/Http/Requests/ModalformRequest.php

<?php

use Illuminate\Foundation\Http\FormRequest;

class ModalformRequest extends FormRequest
{
    public function rules()
    {
        return [
            'name'      => 'bail|required|string|between:2,20',        
            'phone'     => 'bail|required',
            'email'     => 'bail|email:rfc|nullable',
            'address'   => 'bail|string|max:100|nullable',
            'message'   => 'bail|string|max:500|nullable',
            'captcha'   => 'required|captcha',
        ];
    }
}

validation of captcha will done by captcha_check() method not in Laravel validator function. this method will return a boolean response for validation. you should pass the code to this method like this : if (captcha_check($request->captcha_input) == false) { return back()->with('invalid-captcha','captcha is invalid'); }