
Реєстрація в Laravel з перевіркою email. Події і слухачі
10.06.2018 13:20 | Laravel
Як на мене, реєстрація «з коробки» в Laravel реалізовано добре, але є один нюанс - користувач може ввести неіснуючий email і це жодним чином не перевіряється. Пропоную трохи змінити процес реєстрації і заодно розглянути використання подій і слухачів.
Вважаю, найбільш поширеним варіантом верифікації email-у є відправка посилання активації облікового запису на вказану при реєстрації поштову скриньку. Але ж можна зробити і простіше - замість того, щоб відправляти посилання активації, відправити користувачеві згенерований тимчасовий пароль. Зрозуміло, що при цьому не треба вимагати введення пароля при реєстрації, але слід надати користувачеві можливість його зміни в особистому кабінеті.
Приступимо до реалізації цієї ідеї в Laravel 5.6. Перш за все, створимо контролери та вью аутентифікації, виконавши в консолі команду:
php artisan make:auth
Поля вводу і підтверждення пароля в форме реєстрації нам більше не потрібні, тому йдемо у файл resources/views/auth/register.blade.php
та видаляємо їх, після чого форма буде виглядати так:
<form method="POST" action="{{ route('register') }}">
@csrf
<div class="form-group row">
<label for="name" class="col-md-4 col-form-label text-md-right">
{{ __('Name') }}
</label>
<div class="col-md-6">
<input
id="name"
type="text"
class="form-control{{ $errors->has('name') ? ' is-invalid' : '' }}"
name="name"
value="{{ old('name') }}"
required
autofocus
>
@if ($errors->has('name'))
<span class="invalid-feedback">
<strong>{{ $errors->first('name') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">
{{ __('E-Mail Address') }}
</label>
<div class="col-md-6">
<input
id="email"
type="email"
class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}"
name="email"
value="{{ old('email') }}"
required
>
@if ($errors->has('email'))
<span class="invalid-feedback">
<strong>{{ $errors->first('email') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Register') }}
</button>
</div>
</div>
</form>
Окрім цього, в контролері реєстрації – app/Http/Controllers/Auth/RegisterController.php
- слід видалити валідацію поля паролю і згенерувати випадковий пароль, який ми запишемо в додану нами ж властивість класу $generatedPassword
. Пароль потрібно «запам'ятати» для того, щоб згодом відправити його користувачеві. Ну а хеш пароля збережемо в базі даних:
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
class RegisterController extends Controller
{
use RegistersUsers;
protected $generatedPassword;
protected $redirectTo = '/login';
public function __construct()
{
$this->middleware('guest');
}
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users'
]);
}
protected function create(array $data)
{
$this->password = str_random(8);
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($this->password),
]);
}
}
Є ще один важливий момент - в Laravel відразу після реєстрації користувач авторизований, що не узгоджується з нашою ідеєю. Тому в RegisterController
перезавантажимо метод registered()
(цей метод визначений в трейті RegistersUsers
), в якому пропишемо лог-аут та редирект на сторінку входу з повідомленням про відправлення листа з паролем:
protected function registered(Request $request, $user)
{
$this->guard()->logout();
return redirect($this->redirectPath())
->withSuccess('Thanks for registration! The password has been sent to your email.');
}
Відправкою пошти займемося пізніше, а поки створимо подію реєстрації, виконавши в консолі команду:
php artisan make:event Auth/UserRegistered
В результаті буде створено файл app/Events/Auth/UserRegistered.php
. Відкриваємо його і редагуємо. Код повинен виглядати наступним чином:
<?php
namespace App\Events\Auth;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use App\User;
class UserRegistered
{
use Dispatchable, SerializesModels;
public $user;
public $password;
public function __construct(User $user, $password)
{
$this->user = $user;
$this->password = $password;
}
}
Тобто в конструктор ми передаємо модель користувача і пароль, які запишемо у відповідні публічні властивості і пізніше будемо використовувати в слухачі для відправки пошти.
Наступний крок - створення слухача. Зробимо це за допомогою команди:
php artisan make:listener Auth/SendRegisterNotification --event=Auth/UserRegistered
Оскільки в слухачі нам знадобиться клас для відправки пошти, відразу ж створимо і його. При цьому згенеруємо markdown-шаблон для листа:
php artisan make:mail Auth/RegistrationEmail --markdown=emails.auth.registration
Відкриваємо створений на попередньому кроці слухач і прописуємо відправку пошти:
<?php
namespace App\Listeners\Auth;
use App\Events\Auth\UserRegistered;
use App\Mail\Auth\RegistrationEmail;
use Mail;
class SendRegisterNotification
{
public function handle(UserRegistered $event)
{
Mail::to($event->user->email)->send(new RegistrationEmail($event->user, $event->password));
}
}
Тепер треба зареєструвати слухач події, і, як каже документація, найкращим місцем для цього є провайдер сервісу подій (app/Providers/EventServiceProvider.php
):
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Event;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
'App\Events\Auth\UserRegistered' => [
'App\Listeners\Auth\SendRegisterNotification',
],
];
public function boot()
{
parent::boot();
}
}
Настала черга попрацювати з поштою. Заходимо у файл app/Mail/Auth/RegistrationEmail.php
і вносимо необходні зміни. Якщо точніше - також передамо в конструктор модель користувача і пароль і знову запишемо їх в публічні властивості для того, щоб до них можна було звертатися в шаблоні листа. Крім цього, реалізуємо метод build()
, в якому сконструюємо лист:
<?php
namespace App\Mail\Auth;
use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class RegistrationEmail extends Mailable
{
use Queueable, SerializesModels;
public $user;
public $password;
public function __construct(User $user, $password)
{
$this->user = $user;
$this->password = $password;
}
public function build()
{
return $this->markdown('emails.auth.registration')
->subject(config('app.name') . ": Registration Notification")
->from(config('mail.from.address'));
}
}
Що стосується шаблону листа, як ми і просили, Laravel створив файл registration.blade.php
в директорії resources/emails/auth
. Зрозуміло, в шаблоні можна розмістити що завгодно, я просто додам пару слів подяки за реєстрацію, вкажу згенерований пароль і додам кнопку для входу на сайт:
@component('mail::message')
# Welcome!
Thanks for signing up. This is the password we generated for you: **{{ $password }}**
You can change it in your profile.
@component('mail::button', ['url' => route('login')])
Login
@endcomponent
Thanks,<br>
{{ config('app.name') }}
@endcomponent
І останній крок - повернутися в контролер реєстрації і у методі registered()
додати подію. Тепер код даного методу буде виглядати так:
protected function registered(Request $request, $user)
{
event(new UserRegistered($user, $this->password));
$this->guard()->logout();
return redirect($this->redirectPath())
->withSuccess('Thanks for registration! The password has been sent to your email.');
}
Що ж, справу зроблено. Тепер кожен раз при реєстрації нового користувача йому на пошту буде відправлятися лист з паролем. І якщо хто-небудь вирішить вказати «лівий» email, він просто не зможе увійти на сайт для будь-яких дій. Ми ж в свою чергу знаємо, що поштові скриньки, скажімо так, - активних користувачів - реальні.
Треба розуміти, що представлений тут код, можна допрацювати на свій розсуд. Наприклад, ми не хочемо, щоб в базі залишалися дані про всіх тих, хто вказав неіснуючий email. В цьому випадку в таблицю користувачів можна додати поле activated
, яке за замовчуванням дорівнюватиме false
і змінювати його на true
, тільки в разі, якщо користувач протягом доби змінить пароль. Якщо ж це не відбудеться - просто видаляти запис. Зрозуміло, крім пароля в лист також треба буде додати інформацію про те, що пароль буде дійсний протягом доби. Можна також в форму реєстрації додати капчу. Загалом, що стосується доопрацювання, все залежить від потреб - комусь потрібно одне, а кому-то інше.