Информация! Пример написан с использованием Bootstrap стилей и скриптов. При необходимость html, css и js Вы можете заменить на свой, это не повлияет на общую логику работы примера.
В конечном результате получи такую форму обратной связи:
И так приступим.
Создадим модель для формы обратной связи в папке /protected/models/
, имя класса ContactForm
, имя файла модели ContactForm.php
, код модели (/protected/models/ContactForm.php
):
<?php class ContactForm extends CFormModel { public $name; public $email; public $subject; public $body; public $verifyCode; public function rules() { return array( array('name, email, body, verifyCode', 'required'), array('email', 'email'), array('verifyCode', 'captcha', 'on'=>'new'), ); } public function attributeLabels() { return array( 'verifyCode' => 'Капча', 'name' => 'Имя', 'email' => 'Э-почта', 'body' => 'Сообщение', ); } }
Теперь создадим виджет для всплывающего окна формы обратной связи в папке с виджетами /protected/widgets/
, имя класса FBModal
, имя файла модели FBModal.php
, код виджета (/protected/widgets/FBModal.php
):
<?php class FBModal extends CWidget { public function init() { $fBForm = new ContactForm('new'); $cs = Yii::app()->clientScript; $cs->registerScriptFile(Yii::app()->theme->baseUrl . '/js/fbModal.js', CClientScript::POS_END); //выводить новую капча при перезагрузки страницы Yii::app()->controller->createAction('captcha')->getVerifyCode(true); $this->render('webroot.themes.'.Yii::app()->theme->name.'.widgets.FBModal.fBForm', array('fBForm' => $fBForm)); } }
Создадим представление (view) для виджета FBModal
в папке /themes/themeName/widgets/FBModal/
, имя файла fBModal.php
, с кодом модального окна (/themes/themeName/widgets/FBModal/fBModal.php
):
<!-- Модальное окно формы обратной связи --> <div class="modal fade" id="fbModal" tabindex="-1" role="dialog" aria-labelledby="fbModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> <h4 class="modal-title" id="myModalLabel">Форма обратной связи</h4> </div> <?php $form = $this->beginWidget('CActiveForm', array( 'id' => 'uFBForm', 'action' => '/site/sendFBForm', 'enableAjaxValidation' => true, 'enableClientValidation' => true, 'method' => 'POST', 'clientOptions' => array( 'validateOnSubmit' => true, 'validateOnChange' => false, 'validateOnType' => false, 'afterValidate' => 'js:function(form, data, hasError) {if (!hasError) {fbModalSend();}}', 'errorCssClass' => 'has-error', ), 'errorMessageCssClass' => 'small text-danger', 'htmlOptions' => array('class' => 'contact-form', 'role' => 'form'), )); ?> <div class="modal-body"> <div class="form-group"> <?php echo $form->labelEx($fBForm, 'name'); echo $form->textField($fBForm, 'name', array( 'class' => 'form-control', 'placeholder' => 'Введите Ваше имя' )); echo $form->error($fBForm, 'name'); ?> </div> <div class="form-group"> <?php echo $form->labelEx($fBForm, 'email'); echo $form->textField($fBForm, 'email', array( 'class' => 'form-control', 'placeholder' => 'Введите E-mail', )); echo $form->error($fBForm, 'email'); ?> </div> <div class="form-group"> <?php echo $form->labelEx($fBForm, 'body'); echo $form->textArea($fBForm, 'body', array( 'rows' => 3, 'cols' => 40, 'class' => 'form-control', 'placeholder' => 'Введите текст сообщения' )); echo $form->error($fBForm, 'body'); ?> </div> <div class="text-right"> </div> <?php if (CCaptcha::checkRequirements()): ?> <div class="form-group"> <?php echo $form->labelEx($fBForm, 'verifyCode'); ?> <div class="row"> <div class="col-md-4"> <?php $this->widget('CCaptcha', array( 'buttonLabel' => '<br/>Новый код', 'id' => 'newCode' )); ?> </div> <div class="col-md-8"> <?php echo $form->textField($fBForm, 'verifyCode', array( 'class' => 'form-control', 'placeholder' => 'Введите текст указанный на картинке') ); echo $form->error($fBForm, 'verifyCode'); ?> </div> </div> </div> <?php endif; ?> </div> <div class="modal-footer"> <?php echo CHtml::htmlButton('Отмена', array( 'type' => 'reset', 'class' => 'btn btn-default', 'data-dismiss' => 'modal' )); echo CHtml::submitButton('Отправить', array( 'class' => 'btn btn-primary' )); ?> </div> <?php $this->endWidget(); ?> </div> </div> </div> <!-- Всплывающее окно с информацией об успешной отправке сообщения --> <div class="modal fade" id="fbModalOk" tabindex="-1" role="dialog" aria-labelledby="fbModalOkLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> <h4 class="modal-title" id="myModalLabel">Форма обратной связи</h4> </div> <div class="modal-body"> <p>Сообщение успешно отправлено.</p> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">Закрыть</button> </div> </div> </div> </div> <!-- Всплывающее окно с информацией об ошибке при отправке сообщения (при необходимости) --> <!-- ... -->
JavaScript файл для виджета (подключается в классе виджета FBModal
, строка 9). Путь к файлу /themes/themeName/js/
, имя файла fbModal.js
. Скрипт скрывает модальное окно с формой обратной связи после успешной отправки сообщения, очищает поле с текстом сообщения и выводит окно с информацией об успешной отправке сообщения. Код (/themes/themeName/js/fbModal.js
):
function fbModalSend() { $('#fbModal').modal('hide'); $('#fbModalOk').modal('show'); $("#ContactForm_body").val(""); $("#ContactForm_verifyCode").val(""); $.ajax({ url: "/site/captcha?refresh=1", dataType: 'json', cache: false, success: function(data) { $('#yw1').attr('src', data['url']); $('body').data('captcha.hash', [data['hash1'], data['hash2']]); //обновим капчу после успешной отправки $('#newCode_button').click(); } }); }
Настройка капчи. Так как у нас в форме обратной связи используется стандартная капча фреймворка, а наш виджет может вызываться в любой части проекта (например в модуле), то внесем настройки капчи в класс предок контроллеров Controller
который находиться в папке /protected/components/
, файл Controller.php
, код (/protected/components/Controller.php
):
<?php class Controller extends CController { //... public function actions() { return array_merge(parent::actions(), array( 'captcha' => array( 'class' => 'CCaptchaAction', 'backColor' => 0xFFFFFF, 'maxLength' => 4, 'minLength' => 3, //'clickableImage' => true, //'width'=> 200, //'height'=> 50, ))); } //... }
Отправка формы обратной связи в контролере SiteController
(для отправки формы обратной связи Вы можете использовать любой удобный для Вас контроллер). Код (/protected/controllers/SiteController.php
):
<?php class SiteController extends Controller { //... public function actions() { return array_merge(parent::actions(), array( 'page' => array( 'class' => 'CViewAction', ), )); } //... public function actionSendFBForm() { if (Yii::app()->request->isAjaxRequest) { $model = new ContactForm('ajax'); if (isset($_POST['ContactForm'])) { $model->attributes = $_POST['ContactForm']; if ($model->validate()) { $message = '<p>Э-почта: ' . $model->email . '</p>' . '<p>Имя: ' . $model->name . '</p>' . '<p>Сообщение: ' . $model->body . '</p>'; // ... отправка сообщения администратору ... echo CActiveForm::validate($model); Yii::app()->end(); } else { echo CActiveForm::validate($model); } } Yii::app()->end(); } else { throw new CHttpException(404, 'Указанная запись не найдена'); } } //... }
Внимание! Обратите внимание на функцию actions
, она возвращает массив с использованием функции php array_merge
. Это сделано для того что бы не потерять настройки капчи, которые находятся в классе родителе Controller
. Если этого не сделать, то при обращении к действиям (action) контроллера SiteController
Вы получите ошибку в виде исключения с текстом: Действие CCaptchaValidator.action "captcha" задано неверно: действия не найдено в текущем контроллере. Такой способ возврата массива необходимо использовать во всех контроллерах в которых будет присутствовать форма обратной связи и которые будут вызывать метод actions
. Альтернативный способ задать параметры капчи без использования array_merge
:
<?php class SomeController extends Controller { //... public function actions() { return array( 'captcha' => array( 'class'=>'CCaptchaAction', 'backColor'=>0xFFFFFF, 'maxLength'=> 4, 'minLength'=> 3, ), 'page' => array( 'class' => 'CViewAction', ), ); } //... }
Либо не используйте стандартную капчу (или вообще не используйте капчу).
Вызов формы обратной связи:
<!-- Ссылка(кнопка) вызова модального окна с формой обратной связи --> <a href="#" data-toggle="modal" data-target="#fbModal">Обратная связь</a> <?php //Подключение виджета с формой обратной связи $this->widget('application.widgets.FBModal'); ?>
Информация! Не забудьте подключить Bootstrap стили и скрипты для работы всплывающего (модального) окна. Пример:
<?php //... $cs = Yii::app()->clientScript; $cs->registerCssFile(Yii::app()->theme->baseUrl . '/css/bootstrap.min.css'); $cs->registerCssFile(Yii::app()->theme->baseUrl . '/css/style.css'); //... $cs->registerScriptFile(Yii::app()->theme->baseUrl . '/js/ie8-responsive-file-warning.js', CClientScript::POS_END, array('media' => 'lt IE 9')); $cs->registerScriptFile(Yii::app()->theme->baseUrl . '/js/ie-emulation-modes-warning.js', CClientScript::POS_END); $cs->registerScriptFile('https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js', CClientScript::POS_END, array('media' => 'lt IE 9')); $cs->registerScriptFile('https://oss.maxcdn.com/respond/1.4.2/respond.min.js', CClientScript::POS_END, array('media' => 'lt IE 9')); $cs->registerScriptFile(Yii::app()->theme->baseUrl . '/js/bootstrap.min.js', CClientScript::POS_END); $cs->registerScriptFile(Yii::app()->theme->baseUrl . '/js/ie10-viewport-bug-workaround.js', CClientScript::POS_END); //... $cs->coreScriptPosition = CClientScript::POS_END; //...
или
<link rel="stylesheet" type="text/css" href="/themes/themeName/css/bootstrap.min.css" /> <link rel="stylesheet" type="text/css" href="/themes/themeName/css/style.css" /> <!-- ... --> <script type="text/javascript" src="/themes/themeName/js/ie8-responsive-file-warning.js" media="lt IE 9"></script> <script type="text/javascript" src="/themes/themeName/js/ie-emulation-modes-warning.js"></script> <script type="text/javascript" src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js" media="lt IE 9"></script> <script type="text/javascript" src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js" media="lt IE 9"></script> <script type="text/javascript" src="/themes/themeName/js/bootstrap.min.js"></script> <script type="text/javascript" src="/themes/themeName/js/ie10-viewport-bug-workaround.js"></script> <!-- ... -->
Все, форма обратной связи во сплывающем окне - готова. Дальнейшие доработки на Ваш вкус)
Комментарии
а где можно посмотреть js файлы?
ОтветитьВсе js скипты из bootstrap.
ОтветитьВзять их можно отсюда: http://getbootstrap.com/getting-started/#download
Или из любого стандартного примера bootstrap: http://getbootstrap.com/getting-started/#examples
Отдельный компонент всплывающего окна тут: http://getbootstrap.com/javascript/#modals
в коде имеются ошибки
ОтветитьГде именно? И что за ошибки?
ОтветитьДелала полностью по Вашей инструкции, возникла ошибка "Виджет FBModal не может найти представление "webroot.themes..widgets.FBModal.fBForm".", в чем может быть причина?
Ответитьвью fBForm должно лежать в папке
Ответитьwww/themes/названиеВашейТемы/widgets/FBModal
файл
fBForm.php
У Вас так?
www - корень сайта
Ответитьда все так, по ошибке как будто бы нет имени темы webroot.themes..widgets.FBModal.fBModal
ОтветитьУ Вас в файле
Ответить/protected/config/main.php
параметрtheme
задан?Пример:
return array(
'theme' => 'bootstrap',
'sourceLanguage' => 'en_US',
//...
);
Если нет, то добавьте его или задайте имя темы вместо
Yii::app()->theme->name
:$this->render('webroot.themes.названиеТемы.widgets.FBModal.fBForm', array('fBForm' => $fBForm));
удалось решить таким образом $this->render('webroot.themes.'.Yii::app()->theme->getName().'.widgets.FBModal.fBModal', array('fBForm' => $fBForm));
ОтветитьОшибка исчезла, однако форма по нажатию на ссылку не открывается
посмотрела в инспекторе виджет не подключается с формой, с чем это может быть связано?
ОтветитьТочно содержимое fBForm.php не выводится? Попробуйте вместо кода формы вывести что-нибудь другое, например текст.
Ответитьда, проблема именно в том, что содержимое не выводится, страница на этом месте стоит и дальше не грузит
ОтветитьЯсно, я сегодня правил код виджета, строка 86 должна содержать следующий код:
Ответить<?php endif; ?>
Но я точно не помню, какая строка. Скопируйте код еще раз и вставьте у себя, если признаки будут те же, я проверю код примера еще раз
Добавьте в index.php:
Ответитьini_set('error_reporting', E_ALL);
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL', 3);
Что бы видеть все ошибки
$form = $this->beginWidget('CActiveForm', array(
Ответить'id' => 'uFBForm',
хотелось бы спросить, а откуда берется этот 'id' => 'uFBForm',
id формы, здесь он не на что не влияет. У Вас получилось решить проблему с формой?
Ответитьда, проблему с формой решить удалось, осталась одна проблема, после отправки сообщения, не отображается сообщение об успешной отправке
ОтветитьОкно с формой закрывается после отправки?
ОтветитьВ чем была проблема с формой?
проблема была в конфликте скриптов, то есть чисто мой косяк, а вот окно не закрывается, просто весит без признаков жизни, но письма идут на почту
ОтветитьЯ думаю у вас контроллер не отдает ответ. После отправки запроса на сервер что консоль в фаербаге говорит?
ОтветитьПосле отправки письма сделайте вывод
Ответитьecho CActiveForm::validate($model);
или
echo '[]';
Уважаемые пользователи! Если Вы задавали вопрос в комментариях и Вам ответили, то отпишитесь о результатах. Зачастую ответчик ждет обратной связи.
ОтветитьСпасибо большое за статью, все работает отлично, в моем случае просто был конфликт скриптов, единственное, у меня не сработала эта строка $this->render('webroot.themes.'.Yii::app()->theme->name.'.widgets.FBModal.fBForm', array('fBForm' => $fBForm));, но проблема решилась явным написанием названия тему вместо Yii::app()->theme->name
ОтветитьЕще раз спасибо автору, очень помог
Всегда рады помочь!
ОтветитьПростите за мою глупость... Но а где определяется почтовый адрес, куда шлется все это?
ОтветитьМетод
ОтветитьactionSendFBForm
, строка 27:// ... отправка сообщения администратору ...
Тут код отправки, например:
$name = '=?UTF-8?B?' . base64_encode($model->name) . '?=';
$subject = '=?UTF-8?B?Сообщение ФОС?=';
$headers = "From: $model->name <{$model->email}>\r\n" .
"Reply-To: {$model->email}\r\n" .
"MIME-Version: 1.0\r\n" .
"Content-Type: text/plain; charset=UTF-8";
mail(yourEmail@example.com, $subject, $message, $headers);
Как то так)
У вас есть видеоуроки с исходниками на эту тему
ОтветитьЗдравствуйте, Жакс! Видео пока нет, такая идея обдумывается уже давно, пока видео уроки только в планах
ОтветитьЗдравствуйте, а где исходники на файлы и демо?
ОтветитьДобрый день, здесь без демо и исходников, все в статье)
ОтветитьА для YII2 есть пример?
ОтветитьВот: Yii2, форма обратной связи во сплывающем (модальном) окне
ОтветитьЗдравствуйте, спасибо на статью, вопрос есть возможность добавить в форму загрузку файлов, например картинок только png, jpg?
ОтветитьИ еще один вопрос, если пользователь авторизован вместо логина подставляю Yii::app()->user->name; а вот как подставить email утруждаюсь?
ОтветитьВроде Yii::app()->user->title
ОтветитьСпасибо, разобрался <?=$usersModel->email;?>?, а как по первому вопросу по загрузке файлов?
ОтветитьНе знаю, надо экспериментировать. Я тут мимо проходил)
Ответить