Подтверждение регистрации и верификация email в Laravel 5.6

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

Вполне себе нормальное и стандартное решение. А можно ли еще проще? Для того, чтобы ответить на этот вопрос, необходимо уже включать мозг и думать. А как можно по другому решить эту задачу. И для этого мы присмотримся к нашей таблице users.

Давайте создадим нового пользователя и посмотрим в таблицу, что там было создано. Дата создания и дата редактирования совпадают. Токен для запоминания пользователя пустой. Но и само собой понятно, что статус пользователя равен нолю. Что дает нам эта информация? На самом деле очень многое. Будем использовать зависимость пустого токена и нулевого статуса.

В методе регистрации нового пользователя создаётся запись в базе и передаётся в новый объект для отправки сообщения:

// Send user message for activation account.
Mail::to($newUser)->send(new ActivateAccount($newUser));
В почтовом классе я формирую ссылку для активации аккаунта:
// Create activation link.
$activationLink = route('activation', ['id' => $this->user->id, 'token' => md5($this->user->email)]);

Если статус нулевой и токен также нулевой, то это новый пользователь, который ещё не подтверждал свой email. А если токен не нулевой, а статус нулевой, то это означает, что адрес был ранее подтвержден, но пользователь в последствии был забаннен. А если и статус равен единице и есть токен, то пользователь уже успешно активирован ранее.

Полностью класс для отправки email выглядит так:

<?php
namespace App\Mail;
use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class ActivateAccount extends Mailable
{
use Queueable, SerializesModels;
// User data.
protected $user;
/**
* Create a new message instance.
*
* @param \App\User $user
*/
public function __construct(User $user)
{
$this->user = $user;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
// Create activation link.
$activationLink = route('activation', [
'id' => $this->user->id,
'token' => md5($this->user->email)
]);
return $this->subject(trans('interface.ActivationAccount'))
->view('emails.activate')->with([
'link' => $activationLink
]);
}
}

Передаём линк в шаблон и формируем текст для пользователя.  Итак, что значит этот линк. Всё просто, указываем хост для ссылки, потом действие для роута:

// Activation user.
Route::get('activate/{id}/{token}', 'RegistrationController@activation')->name('activation');

Id пользователя и токен — это переменные. Id нужен, чтобы не прогонять всю базу в поисках совпадения email адреса.

Дальше, когда пользователь кликает на ссылку, алгоритм действий простой. Пробуем получить пользователя из базы. Если его нет, то ошибка, если есть, то проверяем пустой ли токен для запоминания пользователя. Если пустой, то хэшируем пользовательский email и сравниваем с токеном из адресной строки. Если они совпадают, то активируем пользователя и тут же залогиниваем используя токен запоминания пользователя. Вот сам метод активации:

/**
* Make user activation.
*/
public function activation($userId, $token)
{
$user = User::findOrFail($userId);
// Check token in user DB. if null then check data (user make first activation).
if (is_null($user->remember_token)) {
// Check token from url.
if (md5($user->email) == $token) {
// Change status and login user.
$user->status = 1;
$user->save();
\Session::flash('flash_message', trans('interface.ActivatedSuccess'));
// Make login user.
Auth::login($user, true);
} else {
// Wrong token.
\Session::flash('flash_message_error', trans('interface.ActivatedWrong'));
}
} else {
// User was activated early.
\Session::flash('flash_message_error', trans('interface.ActivatedAlready'));
}
return redirect('/');
}

А если пользователь еще не подтвердил свой email, то как лучше ему выводить об этом сообщение, в случае если он все-так попробует войти на сайт с неподтвержденным адресом? В контроллере в методе залогинивания можно проводить проверку, что-то типа такого:

// Check user status.
if (!$auth->status) {
\Session::flash('flash_message_error', 'Ваш аккаунт не активен!');
return redirect($this->redirectTo);
}

Это всё! И не нужно никаких дополнительных таблиц, полей, методов, моделей и связей!