×

Делаем форму авторизации в модальном окне

Делаем форму авторизации в модальном окне

При создании сайта на Laravel, возникла задача выполнение механизма авторизации и регистрации с выводом формы модальном окне. К примеру, при попытке оставить отзыв не аутентифицированным пользователем, в модальном появляется форма с предложением авторизации или регистрации. Так же, в зависимости от результата, пользователь в модальном окне получает уведомление об успешной авторизации/регистрации или форму с выводом ошибок валидации, без перезагрузки страницы, с сохранением данных указанных полях формы.

В Laravel из коробки, по дефолту уже предусмотрен удобный механизм валидации данных. При стандартном механизме, при наличии ошибок валидации данных формы, Laravel редиректит ответ со статусом ошибки 422, при удачном прохождении валидации со статусом 200.

Однако в нашем случае, стандартный механизм валидации не подходит, так как редиректа фактически не происходит. Каждый раз, при получении данные формы обрабатываются, из шаблонов view – формируется HTML код формы, даже наличии ошибок валидации отправляются клиенту как новый ответ со статусом 200.

Но ничто не мешает указать статус и заголовки ответа сервера самостоятельно, есть ошибка: статус ответа 422, если нет со статусом 200. И уже на клиенте в зависимости от полученных данных, статусов ответа сервера, обработать ответ JS скриптом.

Для вывода модальных окон используется библиотек на нативном JS – Fancybox 4, способы подключения и использования хорошо описаны в документации, не будем на этом останавливаться. По этой же причине, опустим процесс установки самого фреймворка Laravel, и пакета для аутентификации, на пример Laravel Breeze. Это просто и хорошо документировано.

Поэтапно процесс авторизации пользователя с выводом формы авторизации в модальном окне происходит так.

Определяем роуты Laravel

//../routes/web.php
Route::get('/user-auth', [App\Http\Controllers\Front\UserController::class, 'userAuth'])->name('user.auth');//вызов формы авторизации
Route::post('/user-login', [App\Http\Controllers\Front\UserController::class, 'userAuthStore'])->name('user.auth.store');// роут 	

Роуты Laravel для вывода формы авторизации (метод GET) и обработки данных формы (метод POST). Данные роуты обращаются к методам контроллера UserController.

Создаем контроллер и методы:

php artisan make:controller Front/UserController	

Метод контроллера UserController вызова формы.

class UserController extends Controller
{
    public function userAuth()
    {
        return view('auth_ajax.login');
    }
	//...........
}	

JS код вызова формы авторизации


//по клику вызывается функция getLogIn
document.getElementById('authBtn').addEventListener('click', function (event){
    event.preventDefault();
    getLogIn(); //Функция для обработки формы по клику
});  

let fancybox; // переменная для сохранения состояния модального окна fancybox.
//функция getLogIn - обрабатывает маршрут, отправляет GET запрос получает и обрабатывает ответ ответ сервера - HTML код форм авторизации
function getLogIn(){
	fetch('{{ route('user.auth') }}')
		.then((response) => {
			return response.text();
		})
		.then((data) => {
			fancybox = Fancybox.show([{ src: data, type: "html" }]);// вывод полученных данных сервера в модальном окне
			sendLogin(); //функция для обработки полученной формы авторизации.
	});
}

JS код вызова формы, тут все просто, если посетитель не авторизован, по клику срабатывает событие и обрабатывается маршрут вывода формы.

В данном случае, по клику выполняется функция getLogIn(), которая отправляет сетевой fetch запрос на сервер, получает HTML формы, blade шаблона auth_ajax.login, и выводит данный в модельном окне, с использованием JS библиотеки fancybox. Одновременно вызывается функция - sendLogin().

function sendLogin(){
	let valid = null; // переменная для сохранения о наличии ошибок валидации

	const loginForm = document.getElementById('loginForm'); // форма авторизации
	//событие -  обработка события клика по кнопке отправки формы
	document.getElementById('formBtn').addEventListener('click', function (event) {
		event.preventDefault();
		//получаем данные из полей формы авторизации
		let formData = {
				'email': loginForm.querySelector('[name="email"]').value,
				'password': loginForm.querySelector('[name="password"]').value,
		}
		if(loginForm.querySelector('[name="remember"]').checked)  formData.remember = true;
		
		//отправка формы на сервер методом POST, с заголовками, в том числе CSRF - токена
		fetch('{{route('user.auth.store')}}', {
			method: 'POST', 
			headers: {
				'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').content,
				'Content-Type': 'application/json'
			},
			body: JSON.stringify(formData)
		})
		.then(function (response){
			//обрабатываем статус ответа сервера - о прохождении или наличии ошибок валидации
			if(response.status == 422) valid = false; 
			if(response.status == 200) valid = true;
			return response;
		})
		.then( response => response.text())
		.then(data => {
			if(valid){
				//При прохождении валидации сообщение об удачной валидации, которое закрывается через 3 секунды.  
				document.querySelector('.fancybox__content').innerHTML =  data;
				setTimeout(() => fancybox.close(), 3000);
			}
			f(!valid){
				//При неудачной валидации форма с указанием ошибок валидации.
				document.querySelector('.fancybox__content').innerHTML = data;							
			}
		}).catch((error) => {
			console.log(error);
		});
	});
}

Обратите внимание на строку JS функции sendLogin().

document.querySelector('.fancybox__content').innerHTML =  data;

DOM элемент страницы с классом .fancybox__content – является элементом модального окна fancybox – куда помещается HTML код, ответ сервера.

Blade шаблон формы авторизации, auth_ajax.login.

<div class="form_wrap">
	//Вывод ошибок валидации
	@if (isset($errors) && $errors->any())
		<ul class="alert alert-danger">
			@foreach ($errors->all() as $error)
				<li>{{ $error }}</li>
			@endforeach
		</ul>
	@endif
    //
    @csrf		
        <div class="form_block">
            <label class="form_lable" for="email">Email</label>
            <input class="form_input @error('email') is-invalid @enderror" id="email" type="email" name="email" :value="old('email')" required="required" autofocus="autofocus" tabindex="0">
        </div>        
        <div class="form_block">
            <label class="form_lable" for="password">Password</label>
            <input class="form_input @error('password') is-invalid @enderror" id="password" type="password" name="password" required="required" autocomplete="current-password" tabindex="0">
        </div>
        <div class="form_block">
            <label for="remember_me" class="form_lable">
                <input id="remember_me" type="checkbox" class="form_check" name="remember" tabindex="0">
                <span class="form_udertext">Запомнить меня</span>
            </label>
        </div>
        <div class="form_add">
            <div class="form_add_link">
                <a class="form_link" href="{{ route('register') }}" tabindex="0">Зарегистрироваться</a>
                @if (Route::has('password.request'))
                    <a class="form_link" href="{{ route('password.request') }}" tabindex="0">Забыли пароль?</a>
                @endif
            </div>
            <button type="submit" class="formBtn" id="formBtn" tabindex="0">Log in</button>
        </div>
    
</div>	

В данной форме отсутствует атрибут action - так как отправка формы и ответ сервера обрабатывается JS функцией sendLogin().

Стилизовать форму, задать css стили, или использовать классы какого-либо css фреймворка вы можете самостоятельно на свой вкус. Я не использую фреймворк и в данном случае стилизованная мной форма авторизации выглядит так.

Форма авторизации

Метод контроллера UserController обработки формы.


class UserController extends Controller
{
    public function userAuthStore(Request $request)
    {
		// Правила валидации и сообщения об ошибках
        $validator = Validator::make($request->all(), [
            'email' => ['required', 'string', 'email'],
            'password' => ['required', 'string'],
            ],  $messages = [
                'email.required' => 'Поле Email обязательно для заполнения',
                'email.email' => 'Укажите корректный Email адрес',
                'password.required' => 'Укажите Пароль'
            ]
        );
        if ($validator->fails()) { // Проверка на наличия ошибок валидации
            $request->flash(); // Сохранение в сессию данных полей формы
            return response(view('auth_ajax.login')->withErrors($validator)->render(), 422); // ответ сервера, рендерится view - НTML формы с ошибками валидации, со статусом ответа 422
        } else {
			//При успешной валидации, происходит аутентификация пользователя
            if (Auth::attempt([ 
                'email' => $request->email,
                'password' => $request->password,
            ])) {
                return  response('', 200); //Ответ сервер об успешной аутентификации пользователя, со статусом 200
            } else { // данные указанные пользователем соответствуют правилам валидации, но не верные, к примеру неверный email или пароль пользователя 
                $request->flash();
                $errors[0] = "Hе корректный Email или Пароль";
                return response(view('auth_ajax.login')->withErrors($errors)->render(), 422);
            }
        }
    }
}	

В методе userAuthStore – происходит валидация отправленных данных с формы. В случае наличия ошибок валидации, заново отправляется код формы с ошибками валидации, и статусом ответа сервера 422.

При удачном прохождении валидации происходит проверка наличия указанного пользователем email адреса и пароля в базе, в таблице зарегистрированных пользователем. И при их соответствии происходит аутентификация пользователя, в противном случае возвращается сообщение об ошибке.

В данном примере описан процесс создания аутентификации пользователя сайта на Laravel, с выводом формы в модальном окне. Аналогично, работает регистрация пользователя.

Автор: Максим Волков

Комментарии