
Последнее как раз меня и интересовало, т.к. когда мой блог стал достаточно посещаемым, стало «подписываться» много ботов и скрипт стал пытаться рассылать (используя планировщик хостинга) письма на несуществующие адреса.
Поэтому пришлось внедрить функционал подтверждения e-mail. Т.к. делал я его для данного блога, созданного с помощью php-фреймворка Yii2, описывать буду на базе Yii2. Хотя, на самом деле, его легко приспособить к чему угодно, включая чистый PHP.
Кратко о процессе реализации.
После того, как посетитель вашего сайта или интернет-магазина заполнит форму (регистрации/подписки и др.) ему присваивается специальный код, который с другими данными сохраняется в базу данных, а человеку показывается сообщение, что для окончания регистрации было отправлено письмо на указанный им e-mail со ссылкой подтверждения.
Человек, получив данное письмо, переходит по ссылке которая в нем указана. Она ведет на специальную страницу вашего сайта, где скрипт проверяет код (используя GET-параметр адресной строки) и, если такой код присутствует в БД, активирует данную запись (пользователя).
Вот, вкратце и все. Сам код, уже позже, можно использовать, для удаления аккаунта или отписки от сайта. Так же, человек переходит по ссылка на специальную страницу, где скрипт, по данному коду, находит пользователя в БД и удаляет (или деактивирует) его запись.
Реализация.
Прежде всего нужно в таблицу, в которой у вас хранятся данные по подписчикам (или зарегистрированным пользователям), например в subscription добавить 2 поля:activation VARHAR 255, по умолчанию = null
status tinyint, длина 1, по умолчанию = 0
Для изменения структуры базы данных в Yii2, обычно стоит использовать миграции. Но если вы работаете над собственным сайтом, то можно создать данные поля вручную или выполнив SQL запрос:
ALTER TABLE `subscription` ADD `activation` VARCHAR(255) NULL DEFAULT NULL, ADD `status` TINYINT(1) NOT NULL DEFAULT '0';
В activation будет храниться код, сформированный при регистрации пользователя.
В status будет храниться отметка о том, подтвердил ли пользователь свой e-mail (1) или нет (0).
При этом, так же, сделаем, чтобы пользователи и боты не подтвердившие регистрацию удалялись автоматически. На это дадим им 5 дней.
Сама форма подписки, лично у меня, вынесена в отдельный виджет и использует функционал Pjax (сохраняет и обновляет данные без перезагрузки страницы). Про форму можно почитать в другой моей статье.
При отправке формы, данные POST-запросом отсылаются на один из контроллеров. У меня это frontend\controllers\SiteController.php который принимает данные и отправляет в модель на обработку. Вот его код (для примера):
public function actionSubscription(){ $model = new \common\models\Subscription(); if ($model->load(Yii::$app->request->post()) && $model->validate()){ $model->activation(); if ($model->save()) { Yii::$app->response->refresh(); //очистка данных из формы echo "<p style='color:green'>На Ваш e-mail отправлено письмо со ссылкой.<br><br>" . "Перейдите по ней для активации подписки!</p>"; exit; } } else { echo "<p style='color:red'>Ошибка оформления подписки.</p>"; //Проверяем наличие фразы в массиве ошибки if( isset($model->errors['email']) && strpos($model->errors['email'][0], 'уже занято') !== false) { echo "<p style='color:red'>Вы уже подписаны!</p>"; } } exit; }Тут стоит обратить внимание на строку
$model->activation();которая вызывает метод activation() в котором генерируется код для данного пользователя и сохраняется в БД вместе с другими данными:
public function activation(){ $this->load(Yii::$app->request->post()); $email = Html::encode($this->email); $this->email = $email; $this->addtime = (string) time(); $this->activation = md5($email.time()); // Encrypted email+timestamp //отправка письма для подтверждения email MailKsl::mail_subscription_activation($this->email, $this->activation); }
Данный метод стоит разместить в папке с моделями, а именно в модели обрабатывающей таблицу, в которую мы добавляли поля. У меня это common\models\Subscription.php
Если функционал по сохранению данных формы вы не выносили в модель, то просто добавьте себе строку по формированию кода в действие контроллера:
$this->activation = md5($email.time());тут генерируется индивидуальный код используя e-mail пользователя и текущее время сервера.
В следующей же строке вызывается класс и метод отправки сообщения пользователю. Куда передается его e-mail и код для формирования ссылки.
Пример метода отправки (у меня за отправку отвечает отдельный класс MailKsl):
public static function mail_subscription_activation ($email, $cod){ $absoluteHomeUrl = Url::home(true); //http://ваш сайт $serverName = Yii::$app->request->serverName; //ваш сайт без http $url = $absoluteHomeUrl.'activation/'.$cod; $msg = "Здравствуйте! Спасибо за оформление подписки на сайте $serverName! Вам осталось только подтвердить свой e-mail. Для этого перейдите по ссылке $url"; $msg_html = "<html><body style='font-family:Arial,sans-serif;'>"; $msg_html .= "<h2 style='font-weight:bold;border-bottom:1px dotted #ccc;'>Здравствуйте! Спасибо за оформление подписки на сайте <a href='". $absoluteHomeUrl ."'>$serverName</a></h2>\r\n"; $msg_html .= "<p><strong>Вам осталось только подтвердить свой e-mail.</strong></p>\r\n"; $msg_html .= "<p><strong>Для этого перейдите по ссылке </strong><a href='". $url."'>$url</a></p>\r\n"; $msg_html .= "</body></html>"; Yii::$app->mailer->compose() //->setFrom('admin@klisl.com') //не надо указывать если указано в common\config\main-local.php ->setTo($email) // кому отправляем - реальный адрес куда придёт письмо формата asdf @asdf.com ->setSubject('Подтверждение подписки.') // тема письма ->setTextBody($msg) // текст письма без HTML ->setHtmlBody($msg_html) // текст письма с HTML ->send(); }не забываем подключать используемые классы в начале файла используя «use».
Тут стоит обратить внимание на строку
$url = $absoluteHomeUrl.'activation/'.$cod;в ней мы к URL сайта добавляем строку 'activation/', которая будет обозначать действие для контроллера, обрабатывающего полученный код, ну и далее сам код.
Пример такой строки:
http://site.com/activation/107fcc8a246d4f70c5c846ed7f4ae9e1
Если вы и так отсылали письма при регистрации новых пользователей, то просто вставьте в нее ссылку для активации (и код ее формирования), а остальной код данного метода вам не нужен.
На данном этапе все данные пользователя сохранены в БД, а ему самому выслано письмо со ссылкой, перейдя по которой он сможет завершить процедуру регистрации (подписки).
Теперь нужно создать страницу, ссылку на которую мы отправляем в письме.
Добавляем указанное действие в контроллер frontend\controllers\SiteController.php:
/* * Подтверждение подписки. * В качестве GET-параметра принимается код, который сравнивается с тем, что в таблице subscription * в ячейке activation. При успехе - ставится true в ячейку status. */ public function actionActivation(){ $code = Yii::$app->request->get('code'); $code = Html::encode($code); //ищем код подтверждения в БД $find = \common\models\Subscription::find()->where(['activation'=>$code])->one(); if($find){ $find->status = 1; if ($find->save()) { $text = '<p>Поздравляю!</p> <p>Ваш e-mail подтвержден и Вы оформили подписку на обновления.</p>'; //страница подтверждения return $this->render('activation', [ 'text' => $text ]); } } $absoluteHomeUrl = Url::home(true); return $this->redirect($absoluteHomeUrl, 303); //на главную }
Строкой
$code = Yii::$app->request->get('code');получаем переданный код. Далее проверяем есть ли такой в БД. Если да – ставим true в поле status и на этом пользователь считается полностью зарегистрированным (или подписанным и тд.) о чем ему выводим соответствующее сообщение. Если же такого кода в БД нет – делаем переадресацию на главную страницу.
Для того, что бы по ссылке
http://site.com/activation/107fcc8a246d4f70c5c846ed7f4ae9e1
пользователь попадал в контроллер site и метод activation, нужно добавить правило маршрутизации. Для это в файл frontend\config\main.php в массив urlManager, подмассив rules нужно добавить
'activation/<code:.+>' => 'site/activation',в начало или по крайней мере до строки
'<action:\w+>' => 'site/<action>',
Таким образом мы получим GET параметр с ключом code и значением в качестве переданного кода, который обработает контроллер.
Ну и осталось сделать файл-представление frontend\views\site\activation.php.
Пример такого файла:
<?php use yii\helpers\Html; $this->title = 'Подписка'; $this->registerMetaTag([ 'name' => 'robots', 'content' => 'noindex,nofollow' ]); $this->params['breadcrumbs'][] = $this->title; ?> <h1><?= Html::encode($this->title) ?></h1> <div class="top_block white"> <div class="content"> <?=$text?> </div> </div>
Вызывая метод registerMetaTag() формируем метатег, указывающий поисковым службам, что это служебная страница и ее не нужно индексировать.
У меня на сайте, на этой странице, так же используются блоки и стили Bootstrap + виджеты боковой колонки. Вы сможете настроить вид индивидуально.
На этом процесс регистрации/подписки завершен, а именно – пользователь подтвердил свой e-mail о чем свидетельствует значение true (единица) в поле status. Далее, при выборка пользователей, вы просто проверяете наличие данного значения и если в поле status находится значение false – игнорируете таких пользователей.
Что еще не мешало бы, так это сделать автоматическое удаление пользователей (и ботов) которые не подтвердили свой электронный адрес в течении определенного периода, чтобы не накапливать их в таблице. Ниже я приведу такой скрипт.
У меня на блоге, я реализовал отправку уведомлений о новых статьях подписчикам с помощью планировщика (cron), который запускается в определенное время и делает рассылку. Поэтому такое удаление неподтвержденных пользователей я добавил в скрипт рассылки, так же для автоматического выполнения по расписанию. Вы же, если не используете рассылку и планировщик, можете делать удаление таких пользователей при наступлении какого-то события, например при регистрации нового пользователя.
//Удаление подписчиков, которые не подтвердили свой e-mail в течении 7-и дней. public function deleteSubActivation(){ $today = time(); $old_time = $today - (86400*7); $oldSub = Subscription::find() ->where(['status' => '0']) ->andWhere(['<','addtime', $old_time]) ->all(); foreach($oldSub as $sub){ $sub->delete(); } }В поле addtime таблицы subscription у меня хранится метка времени (в формате Unix) когда текущий пользователь был добавлен.