
после заполнении формы на странице регистрации, пользователь перенаправляется на главную страницу, где ему выводится флэш сообщение «Проверьте свою электронную почту для подтверждения регистрации» (тексты по-умолчанию на английском). Пользователю отправляется письмо со ссылкой, перейдя по которой, в базе данных его статус меняется на «активен», после чего он сможет залогиниться на сайте. Таким образом проверяем указанный им, при регистрации, электронный адрес.
Процесс состоит из нескольких простых пунктов и используется для фреймворка yii2 версии advanced.
1.
В файле миграции которая создает таблицу user («из-коробки» находится в console/migrations/m130524_201442_init.php) меняем значение для поля "status" на "0", что бы, по-умолчанию, пользователь считался не активным:'status' => $this->smallInteger()->notNull()->defaultValue(0),
2.
Создаем новую миграцию – добавляем поле 'email_confirm_token' для хранения токена в таблицу user:<?php use yii\db\Migration; class m180317_182650_add_user_email_confirm_token extends Migration { public function up() { $this->addColumn('{{%user}}', 'email_confirm_token', $this->string()->unique()->after('email')); } public function down() { $this->dropColumn('{{%user}}', 'email_confirm_token'); } }При регистрации будет сгенерирована случайная строка и сохранена в это поле. Потом она будет отправлена пользователю на его электронный адрес в ссылке для перехода.
3.
Корректируем «модель» (сущность) User. По-умолчанию в фреймворке версии advanced уже создан файл User.php в common/models.В начале класса добавляем константу:
const STATUS_WAIT = 5;Данный статус будет означать, что пользователь зарегистрировался но еще не подтвердил свой email.
Меняем метод rules:
public function rules() { return [ ['status', 'in', 'range' => [self::STATUS_DELETED, self::STATUS_WAIT, self::STATUS_ACTIVE]], ]; }
Метод findByUsername меняем на:
public static function findByUsername($username) { return static::findOne(['username' => $username]); }
4.
Контроллер, действие «signup».По-умолчанию, за регистрацию пользователей отвечает метод actionSignup в frontend/controllers/SiteController.php
Меняем данный метод таким образом:
public function actionSignup() { $form = new SignupForm(); if ($form->load(Yii::$app->request->post()) && $form->validate()) { $signupService = new SignupService(); try{ $user = $signupService->signup($form); Yii::$app->session->setFlash('success', 'Check your email to confirm the registration.'); $signupService->sentEmailConfirm($user); return $this->goHome(); } catch (\RuntimeException $e){ Yii::$app->errorHandler->logException($e); Yii::$app->session->setFlash('error', $e->getMessage()); } } return $this->render('signup', [ 'model' => $form, ]); }
Строкой
$form = new SignupForm();подключаем класс валидации формы. Далее, пытаемся загрузить данные из POST запроса и если они прошли проверку, то создается объект класса-сервиса, который и будет заниматься регистрацией.
5.
Сервис регистрации.Основную логику касающуюся регистрации пользователей вынесем в отдельный класс.
Файл common/services/auth/SignupService.php:
<?php namespace common\services\auth; use Yii; use common\entities\User; use common\forms\auth\SignupForm; class SignupService { public function signup(SignupForm $form) { $user = new User(); $user->username = $form->username; $user->generateAuthKey(); $user->setPassword($form->password); $user->email = $form->email; $user->email_confirm_token = Yii::$app->security->generateRandomString(); $user->status = User::STATUS_WAIT; if(!$user->save()){ throw new \RuntimeException('Saving error.'); } return $user; } public function sentEmailConfirm(User $user) { $email = $user->email; $sent = Yii::$app->mailer ->compose( ['html' => 'user-signup-comfirm-html', 'text' => 'user-signup-comfirm-text'], ['user' => $user]) ->setTo($email) ->setFrom(Yii::$app->params['adminEmail']) ->setSubject('Confirmation of registration') ->send(); if (!$sent) { throw new \RuntimeException('Sending error.'); } } public function confirmation($token): void { if (empty($token)) { throw new \DomainException('Empty confirm token.'); } $user = User::findOne(['email_confirm_token' => $token]); if (!$user) { throw new \DomainException('User is not found.'); } $user->email_confirm_token = null; $user->status = User::STATUS_ACTIVE; if (!$user->save()) { throw new \RuntimeException('Saving error.'); } if (!Yii::$app->getUser()->login($user)){ throw new \RuntimeException('Error authentication.'); } } }
Учтите, что пространства имен для User и SignupForm (сверху для директивы use) у вас будут отличаться.
Метод signup будет заниматься созданием нового пользователя в базе данных. Данный метод заменяет одноименный метод из SignupForm (по-умолчанию в frontend\models\SignupForm.php), поэтому от туда его можно удалить.
Метод sentEmailConfirm отправляет регистрирующемуся пользователю письмо со ссылкой для подтверждения регистрации.
Метод confirmation будет использоваться для проверки токена при переходе пользователя по ссылке подтверждения (ссылка будет содержать данный параметр, который будет найден в базе данных). Если токен верный, то поле email_confirm_token очищается:
$user->email_confirm_token = null;и пользователь получает статус «акивен»:
$user->status = User::STATUS_ACTIVE;
Вызовы этих методов сервиса в контроллере обернуты в блок try-catch, и в случае ошибки пользователь увидит соответствующее флэш сообщение, так же ошибки запишутся в логи для администратора.
6.
Письма подтверждения регистрации.Метод sentEmailConfirm класса SignupService отправляет письма пользователю для подтверждения регистрации. При этом используются шаблоны с текстом для html версии - 'user-signup-comfirm-html' и простой текстовой версии – 'user-signup-comfirm-text'. Создаем соответствующие файлы:
Файл common/mail/user-signup-comfirm-html.php:
<?php use yii\helpers\Html; /* @var $user \common\entities\User */ $confirmLink = Yii::$app->urlManager->createAbsoluteUrl(['site/signup-confirm', 'token' => $user->email_confirm_token]); ?> <div class="password-reset"> <p>Hello <?= Html::encode($user->username) ?>,</p> <p>Follow the link below to confirm your email:</p> <p><?= Html::a(Html::encode($confirmLink), $confirmLink) ?></p> </div>
Файл common/mail/user-signup-comfirm-text.php:
<?php /* @var $user \common\entities\User */ $confirmLink = Yii::$app->urlManager->createAbsoluteUrl(['site/signup-confirm', 'token' => $user->email_confirm_token]); ?> Hello <?= $user->username ?>, Follow the link below to confirm your email: <?= $confirmLink ?>
Пользователь получит такой текст сообщения:
Hello Name,
Follow the link below to confirm your email:
http://example.com/site/signup-confirm?token=SjfUrPihsY2peMqb7XB45_TRfBRvadRu
Сама ссылка может отличаться в зависимости от установленных правил маршрутизации в вашем приложении.
Как видим, обрабатывать переход по ней будет SiteController с действием signup-confirm.
7.
Контроллер, действие «signup-confirm».public function actionSignupConfirm($token) { $signupService = new SignupService(); try{ $signupService->confirmation($token); Yii::$app->session->setFlash('success', 'You have successfully confirmed your registration.'); } catch (\Exception $e){ Yii::$app->errorHandler->logException($e); Yii::$app->session->setFlash('error', $e->getMessage()); } return $this->goHome(); }Данное действие использует все тот-же сервис для регистрации, вызывая его метод confirmation.
8.
Контроллер, действие «login».На данном этапе все уже работает, но может возникнуть ситуация, что пользователь будет пытаться залогиниться еще до того, как подтвердит свой email. Если не поправить действие login, то он увидит сообщение «Incorrect username or password.» что не правильно. Вместо этого покажем ему «To complete the registration, confirm your email. Check your email.». Для этого меняем:
public function actionLogin() { if (!Yii::$app->user->isGuest) { return $this->goHome(); } $form = new LoginForm(); if ($form->load(Yii::$app->request->post())) { try{ if($form->login()){ return $this->goBack(); } } catch (\DomainException $e){ Yii::$app->session->setFlash('error', $e->getMessage()); return $this->goHome(); } } return $this->render('login', [ 'model' => $form, ]); }
Так же в моделе формы – класс LoginForm (по-умолчанию находится в common/models/LoginForm.php) меняем метод login:
public function login() { if ($this->validate()) { $user = $this->getUser(); if($user->status === User::STATUS_ACTIVE){ return Yii::$app->user->login($user, $this->rememberMe ? 3600 * 24 * 30 : 0); } if($user->status === User::STATUS_WAIT){ throw new \DomainException('To complete the registration, confirm your email. Check your email.'); } } else { return false; } }
Это все. Не забудьте в контроллере подключить:
use common\services\auth\SignupService;