CodeIgniter + AJAX. Проверка данных форм без перезагрузки страниц

В прошлой статье речь шла о проверке данных форм с помощью стандартной библиотеки фрэймворка CodeIgniter. Мы рассмотрели основные возможности библиотеки, создали страницу с формой и написали необходимый для ее проверки код.
Сегодня мы посмотрим, как решить эту же задачу без перезагрузки страницы с формой. Естественно будет использоваться технология ajax.
Давайте немного порассуждаем. Чем отличается ajax запрос от обычного запроса?
1) Ajax запрос можно выполнить только с помощью JavaScript.
2) Ответом сервера на обычный запрос должна быть html страница, а на ajax запрос – фрагмент данных (с разметкой или без нее).
Отсюда вывод. Чтобы добавить поддержку ajax нужно в клиентской части написать JavaScript функцию, которая прочтет данные из формы, отправит запрос и обработает ответ сервера, а в серверной части – изменить возвращаемые значения.
Примечание. Чтобы немного упростить себе жизнь, мы будем использовать библиотеку prototype для отправки запросов и обновления страницы.
Переходим к нашему примеру. Как вы помните, в прошлый раз мы создали форму с тремя полями для ввода личных данных пользователя (ник, полное имя, адрес email).
В этот раз мы используем эту же форму, только немного изменим html разметку.
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN">
-
<html xmlns="http://www.w3.org/1999/xhtml" lang="ru">
-
<head>
-
<title>Проверка введенных данных</title>
-
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
-
<script type="text/javascript" src="<?php echo base_url(); ?>prototype.js"></script>
-
<script type="text/javascript" src="<?php echo base_url(); ?>scripts.js"></script>
-
</head>
-
-
<body>
-
<?php echo form_open('#'); ?>
-
<div id="status"></div>
-
<div id="nic_err"></div>
-
<p>Ник: <input type="text" name="usernic" id="usernic" value="" /></p>
-
<div id="name_err"></div>
-
<p>Полное имя: <input type="text" name="fullname" id="fullname" value="" /></p>
-
<div id="email_err"></div>
-
<p>eMail: <input type="text" name="usermail" id="usermail" value="" /></p>
-
<p><input type="button" id="sendbtn" value="Отправить" onclick="sendData(<?php echo "'".base_url()."'"; ?>)" /></p>
-
</form>
-
</body>
-
</html>
Прежде всего, перед каждым полем мы добавили блоки для отображения сообщений об ошибках (теги <div> в строках …). Сами сообщения мы будет вставлять с помощью JS.
Второе изменение коснулось кнопки «Отправить». Теперь она имеет тип button, а не submit и обработчик события onclick, который вызывает функцию отправки данных (sendData).
Сама функция sendData находится в файле scripts.js, который мы подключили в заголовке страницы вместе с библиотекой prototype (строки 6, 7).
Прежде чем, переходить к отправке сообщения необходимо определиться с форматом данных, в котором мы будем получать результаты обработки.
С отправкой данных все довольно просто. Нужно сформировать строку с параметрами обычного POST запроса.
А вот формат результатов обработки гораздо интереснее. Страница нашей формой содержит четыре блока, в которые могут быть вставлены описания ошибок. Удобнее всего вставлять эти данные, если содержимое каждого из полей находится в отдельных переменных (ячейках массива или структуре).
Т.е. нам нужно получить данные в формате, который мы сможем легко преобразовать в набор переменных. Идеально для этой цели подходит JSON.
Примечание. Подробнее почитать об этом формате можно в статье «Передача данных с помощью JSON».
Главное преимущество заключается в том, что и JavaScript и PHP содержат функции для преобразования данных в этот формат и обратно.
Таким образом, наша функция sendData будет примет такой вид.
-
function sendData(baseURL) {
-
//читаем данные из формы
-
var n = $('usernic').value;
-
var fn = $('fullname').value;
-
var e = $('usermail').value;
-
//формируем строку с параметрами запроса
-
var pars = $H({usernic:n, fullname:fn, usermail:e}).toQueryString();
-
//отправляем ajax запрос
-
new Ajax.Request(baseURL + "index.php/main/checkdata_ajax",
-
{method:"post", parameters:pars, onSuccess:parseResponse});
-
}
В параметре функции мы передаем адрес сервера. А в строках с 3-7 мы формируем строку с параметрами.
После этого мы отправляем запрос. Ответ сервера передается функции parseResponse, которая указана в параметре onSuccess запроса.
Теперь рассмотрим функцию parseResponse.
-
function parseResponse(transport) {
-
var data = eval('(' + transport.responseText + ')');
-
//ошибок не было
-
if (data.status == "OK") {
-
$('status').innerHTML = "Данные сохранены";
-
//убираем сообщение об ошибках, если они остались после
-
//предыдущей попытки
-
$('nic_err').innerHTML = "";
-
$('name_err').innerHTML = "";
-
$('email_err').innerHTML = "";
-
}
-
//ошибки были, показываем их описание
-
else {
-
$('status').innerHTML = "Пожалуйста, проверьте введенные данные";
-
$('nic_err').innerHTML = data.nic_err;
-
$('name_err').innerHTML = data.name_err;
-
$('email_err').innerHTML = data.email_err;
-
}
-
}
Во второй строке мы преобразуем строку в формате JSON в структуру данных с именем data (с помощью функции eval).
Наша структура данных имеет такие поля:
status – результат обработки (может принимать два значения: «OK» и «ERR»);
nic_err – описание ошибки в поле «ник»;
name_err – описание ошибки в поле «полное имя»;
email_err – описание ошибки в поле «email».
В строке 4 мы проверяем поле data.status и в зависимости от результатов либо вставляем описание ошибок, либо убираем их.
Теперь рассмотрим серверный скрипт (checkdata_ajax), который должен находится внутри контроллера (main.php).
-
function checkdata_ajax() {
-
$this->load->library('validation');
-
-
$rules['usernic'] = "required|min_length[3]|checknic";
-
$rules['fullname'] = "required";
-
$rules['usermail'] = "required|valid_email";
-
-
$this->validation->set_rules($rules);
-
-
$fields['usernic'] = "ник";
-
$fields['fullname'] = "полное имя";
-
$fields['usermail'] = "адрес email";
-
-
$this->validation->set_fields($fields);
-
-
if ($this->validation->run() == TRUE) {
-
//сохраняем введенные данные (например, в БД)
-
//……….
-
//отправляем браузеру результаты обработки
-
$res['status'] = "OK";
-
}
-
else {
-
$res['status'] = "ERR";
-
$res['nic_err'] = $this->validation->usernic_error;
-
$res['name_err'] = $this->validation->fullname_error;
-
$res['email_err'] = $this->validation->usermail_error;
-
}
-
echo json_encode($res);
-
}
Первая часть метода не изменилась. Действительно, данные приходят в параметрах POST запроса и серверному скрипту без разницы как этот запрос отправлен.
Отличие от предыдущего примера заключается в том, что в этот раз мы не отправляем браузеру страницу, а формируем массив с результатами обработки, преобразовываем его в формат JSON и отправляем браузеру.
Примечание. На основе имен ключей в массиве будет создаваться структура данных в функции parseResponse.
Как видите, добавление поддержки ajax не требует значительных усилий. В чем-то она даже проще классического варианта. Например, нам не нужно заполнять поля формы предыдущими значениями, т.к. введенные значения никуда не денутся (ведь мы все время находимся на одной и той же странице). Главный недостаток – это необходимость написания JavaScript кода, который может быть довольно сложным.
Понравилась статья? Подписывайтесь на продолжение
!
Опубликовано в Ajax, CodeIgniter, PHP Комментарии (48) »
Комментарии (48)
Вы можете отслеживать обсуждение записи с помощью RSS 2.0 ![]()
Вы также можете оставить комментарий, или трекбек с Вашего сайта.









в таком раскладе сильно смущает function checkdata_ajax() в контроллере – что будет если пользователь пойдёт по урл этого экшена?
Получит ответ от сервера (в формате JSON)
Firefox показал их в таком виде:
{"status":"ERR","nic_err":"","name_err":"","email_err":""}
В общем, что и следовало ожидать.
К тому же, обычный посетитель по этой ссылке не перейдет, т.к. для этого нужно просмотреть js-файлы, а взломщик… ну это вопрос защиты (если точнее, то обработки отправленных значений).
Ещё было бы пару фраз о сравнении ajax-фреймворков (или скорее библиотек будет правильней сказать) – было бы вообще отлично. Хотя это темы для отдельных статей, коих в принципе и так хватает в Интернете
Сложно не согласиться
Сравнение ajax-фрэймворков – тема бесконечная, особенно если учесть, что они постоянно развиваются.
По-моему, в сложившейся ситуации важнее навыки работы с конкретным фрэймворком.
Сравнение фреймворков видел здесь:
http://blog.sribna.com/o-proizvoditelnosti-javascript-frameworks.htm
Сам пока ещё не выбрал, т.к. нужно учитывать не только скорость.
Спасибо за ссылку, но я ее уже читал
И вы абсолютно правы в том, что ориентироваться не только на скорость.
Но, тем не менее, разница в скорости между prototype и dojo заставляет задуматься.
Неплохая статья. По поводу checkdata_ajax():
можно сделать двойную функцию типа checkdata_ajax($transport) и в яве что-то типа
var url = 'путь_к_скрипту' + '/ajax', а в поле action ввести тот же путь, только без аякс. В CI уже обработать переменную и т.д.
Зы. Вот только почему-то у меня этим способом:
$res['usernic_error'] = $this->validation->usernic_error;$res['password_error'] = $this->validation->password_error;
$res['usermail_error'] = $this->validation->usermail_error;
вместо ошибок выдает undefined
>> По поводу checkdata_ajax()
Тоже вариант, но все равно, посетитель при желании может сформировать любой запрос и обратиться к ajax функции.
>> выдает undefined
Убедитесь, что вы подключили библиотеку
$this->load->library(‘validation’);и установили поля
$fields['usernic'] = "ник";$fields['fullname'] = "полное имя";
$fields['usermail'] = "адрес email";
$this->validation->set_fields($fields);
С undefind разобрался – очепятки в тексте
Посетитель то может, но это ему ни чего не даст, т.к. он увидит что-то типа:{”status”:”ERR”,”nic_err”:”",”name_err”:”",”email_err”:”"}
Впрочем, как и на любом другом сайте, если неправильно задать запрос.
В таком случае хоть будет работать если у посетителя отключен java в браузере
Как я понял, вы предлагаете обеспечить оба варианта работы с формой (с ajax и без него).
Подход хороший, только работы в 2 раза больше
Именно. Зато работать будет везде. Просто у меня уже все было написано без ajax. А потом наткнулся на эту статью – сделал как тут написано, только через mootools и готово
Хорошее решение.
Отличная статья. В мемориз.
Если интересно, для jQuery есть плагин для валидации любых форм
Спасибо за ссылку.
Этот плагин мне напомнил библиотеку для проверки форм в CodeIgniter. Правда, наверное все такие библиотеки работают принципу – устанавливаем правила, вызываем функцию validate().
Еще не разобрался как прикрутить к нему ajax, но общее впечатление очень хорошее.
hello
Супер!
Статья супер.. А кто знает где можно почитать о проверке введенных на лету?
"на лету" – в смысле без отправки запроса серверу? или с отправкой запросов по мере заполнения формы?
В любом случае такие проверки делаются с помощью JavaScript. Читаем введенное значение, проверяем и выводим результаты. А проверка может быть локальная либо с выполнение запроса к серверу.
Почитать… Наверное, лучше всего какую-нибудь книгу о JavaScript или JS библиотеке (например, jQuery).
Статей и примеров на эту тему очень много, но в них описаны решения конкретных задач, поэтому не знаю что и посоветовать…
Можно погуглить "javascript form validation"
Один из простых способов защитить метод контроллера, который производит запись в базу можно проверят POST массив, если его не существует вообще, то следовательно информацию передавал не JS,а кто то обратился напрямую к методу.
Никогда так не делайте! На любом скриптовом языке, можно в пять-десять строчек написать скрипт, который будет отправлять вам POST с любыми данными, при этом USER-AGENT у него будет IE, или FF. Да и ip будет с немецкого открытого прокси.
Проверять именно данные необходимо всегда!
Я дополню. По большому счету проверок в этом примере недостаточно. Например, нужно проверять еще длину, полученных данных, т.к. существует уязвимость связанная со вставкой в базу данных, длинна которых превышает размер поля.
Сделайте урок по добавлению комментариев на сайт без перезагрузки страници !
Да, да! Именно так надо просить:
1. Используйте повелительное наклонение.
2. В конце поставьте восклицательный знак.
3. Не употребляйте слов "пожалуйста".
Только при соблюдении всех пунктов, у любого мимо проходящего, возникнет желание, сразу сделать, всё что вам надо.
@Борис, спасибо! Все-таки приятно осознавать, что есть люди, которые обращают внимание на подобные "мелочи"
По-поводу урока. Нельзя написать о добавлении комментариев без статей о создании блога (или сайта). А я такой цикл в ближайшее время не планирую.
Кроме того, код добавления комментариев практически не будет отличаться от кода в этой статье. Просто форма будет содержать другие поля. Так что эту статью вполне можно использовать для решения задачи.
А нету ли подобного примера для проверки полей c jQuery?
Возможно вам подойдет этот плагин.
Нет, я о другом спрашиваю.
Нужно сделать чтобы при регистрации проверялось наличии email в списке пользователей. Идеи есть, но затык какой-то, не могу понять как параметр пропихнуть с метод.
Собственно jQuery скрипт:
$(document).ready(function() {$('#emailid').change(function() {
var msg = $('#emailid').val();
$.post("", {message: msg}, function(data) {
console.log(data);
if(data=='OK')
{
$('messg').html('Bad');
}
else $('messg').html('OK');
});
});
});
emailid – проверяемое поле.
Контроллер User
function checkEmail($email){if($_POST && $_POST[$email] != NULL){
$this->usermodel->checkUserByEmail($email);
}
else redirect ('user/item_list');
}
Ну и модель
function checkUserByEmail($email){
$data = array();
$this->db->where('email',$email);
$q = $this->db->get('users');
if($q->num_rows()>0)
{
$data = 'OK';
} else $data = 'NO';
return $data;
}
Сейчас матерится
Получается, что не пролезает переменная с формы. Как это исправить, где я ошибся?
В общем-то разобрался, могу поделится кодом, если кому надо.
Помогла функция
$this->db->last_query();помогающая показать последний запрос, сформированный через Active Record и FirePHPПеременную из формы вы передаете в массиве $_POST, но при этом вы объявили, что методу checkEmail($email) контроллера должен передаваться один параметр (с именем $email), который вы потом пытаетесь использовать при вызове метода модели.
Кроме того, вы пытаетесь найти в массиве $_POST элемент, используя в качестве ключа переменную $email, которая не определена.
В общем, я бы изменил код таким образом
function checkEmail(){ if($_POST && $_POST['email'] != NULL){ //или здесь или в модели добавить проверку $email = $_POST['email']; $this->usermodel->checkUserByEmail($email); } else redirect ('user/item_list'); }А с точки зрения использования библиотек CI правильнее так
function checkEmail(){ if (FALSE !== ($email = $this->input->post('email'))){ //или здесь или в модели добавить проверку $this->usermodel->checkUserByEmail($email); } else redirect ('user/item_list'); }P.S. Для вашего кода переменная $email будет определена если вы выполните запрос вида
sitename.domen/user/checkemail/123
в этом случае
$email == '123'
Вообще, это интересный способ, вот так формировать параметр, в стиле CI. Но, безопасно ли это?
Кстати, нарыл свежий туториал "Создание Пользовательской корзины с помощью CI и jQuery"
А что значит безопасно? Передача данных get или post запросом не менее опасна. Все данные передаются открыто и если кто-то сможет получить доступ к сетевому трафику, то и к этим параметрам он получит доступ тоже.
Защита существует и называется SSL.
Если речь о безопасности вашего скрипта, то тут все просто. Нужно проверять полученные данные. Не зависимо от того, каким способом они были отправлены.
Просто я заменил в одном скринкасте одну забавну штуку
define('IS_AJAX', isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest');
Она не позволяет обратится напрямую к станице, возвращающей значение для скрипта через ajax.
http://www.weblee.co.uk/2009/06/08/simple-jquery-ajax-with-codeigniter-part-2/
Где-то в конце ролика показывается как это работает. Помоему неплохо
Дело в том, что вы все равно можете обратиться к странице, например, с помощью cURL. И при этом установить все нужные заголовки страницы.
Этот код удобный, т.к. позволяет отличить обычный запрос от ajax-запроса, но при этом нужно помнить, что проверять все полученные данные все-равно нужно. Т.е. это не способ защиты.
//сохраняем введенные данные (например, в БД)
А как именно получить доступ к этим самым данным (имеется ввиду к уже прошедшим проверку из которых уже был удален вредоносный код путем xss_clean). Перечитал руководство – везде пишется одно и тоже – как обработать и отобразить ошибки, а что делать конкретно с данными – нет
$usernic = $this->input->post('usernic');
и т.д.
Т.е. просто читаете их из массива $_POST, идея в том, что это чтение будет выполняться только если не возникнет ошибок при проверке данных.
Спасибо, опытным путем понял, что все действия перечисленные в правилах выполняются над $_POST массивом, и все изменения сохоаняются. Таким образом получается, что
$rules['usermail'] = "required|valid_email|xss_clean";эквивалентно$this->input->post('usernic', TRUE);с той лишь разничей, что в первом случае очистка xss будет выполняться вместе с проверкой правил, а во втором – уже после нее.Так же выяснил, что
$rules['usermail'] = "required|valid_email|xss_clean|callback__user_checkвсе действия функции user_check (если таковые производятся,скажем, удаление ссылок или рисунков, а не просто проверяется на валидность) тоже сохранятся в $_POST массиве.Небольшое уточнение. Менять данные в методе _user_check не совсем правильно. Этот метод должен возвращать true или false в зависимости от результатов проверки.
Если нужно изменить данные, то лучше прочитать их (сохранить в переменной) и дальше работать с ней.
Итого имеем, к примеру
$rules['usernic'] = "required|min_length[3]|max_length[10]";Если все норм
if ($this->validation->run() == TRUE) {$usernic = $this->input->post('usernic', TRUE); //очистили от xss
$usernic = user_check($usernic); //выполнили свои дйствия
}
Просто в документации по CI встретил такие приммеры
$rules['usernic'] = "required|xss_clean";А ведь xss_clean не возвращает TRUE or FALSE, поэтому и вышла такая непонятка))
Теперь я понял в чем вопрос заключается
Дело в том, что в перечне правил вы можете использовать практически все фунции php, которые принимают один аргумент. Например, md5, trim и т.п.
Т.е. строка
$usernic = $this->input->post('usernic', TRUE);
совсем не обязательна, если в правилах вы указываете
"…|xss_clean"
Но если нужно использовать функцию сторонней библиотеки, то лучше это сделать после проверки введенных данных.
Например,
... $rules['usernic'] = "required|xss_clean"; ... if ($this->validation->run() == TRUE) { //загружаем библиотеку //получаем данный из post ($usernic = $this->input->post('usernic')) //используем методы библиотеки }А как быть если форма содержит массив checkbox-ов?!
Ну я имею ввиду, если форма содержит такие строки:
Как тогда будет выглядеть функция sendData(), а именно строка с параметрами запроса?
И еще один вопрос! Возможно ли сдалать такую ajax-форму, которая бы отправляла письма с вложениями???
Заранее спасибо!
Тут все зависит от вашего воображения
Наверное, самый удобный вариант – сформировать запрос также как и при обычной отправке.
Т.е. если есть массив с чекбоксами
… type="checkbox" name="my_chekbox[]"…
то строку запроса формируем с помощью цикла:
1) проходим по всем чекбоксам формы
2) если чекбокс отмечен, добавляем его в строку запроса
Например, если отмечено два чекбокса, то в запросе нужно передать
my_chekbox[]:значение_1
my_chekbox[]:значение_2
Этот вариант лучше, т.к. не нужно будет писать два варианта кода серверной (php) части для разбора полученных данных. Т.е. проще сделать форму, которая будет работать с использованием ajax и без него.
Но, как я написал в начале, вас ничто не ограничивает. Например, можно сформировать массив с данными формы, преобразовать его в json формат и отправить в одном параметре.
Что касается отправки файлов. У меня есть статья на эту тему
Как реализовать асинхронную загрузку файлов с помощью JavaScript и PHP
А как получить значение выбранного чекбокса из массива?
Так :
var chek = $('my_chekbox[0]').value;Я бы сделал немного по-другому.
<form action="#" method="post" id="my_form"> <p> <label><input type="checkbox" name="my_checkbox[]" value="1" />1</label> <label><input type="checkbox" name="my_checkbox[]" value="2" />2</label> <label><input type="checkbox" name="my_checkbox[]" value="3" />3</label> <input type="submit" value="Отправить" /> </p> </form> $(function() { $('#my_form').submit(function() { var mc = $('input:checked'); mc.each(function() { alert($(this).val()); }); return false; }); });Спасибо Владимир!
Я в jquery не очень силен, поэтому хотел Вас попросить показать как это прикрутить к скрипту
scripts.js, который Вы рассматриваете в начале этой статьи!Извините, я забыл, что в этом примере использовал prototype, а не jQuery. Последнее время я практически полностью перешел на jQuery, т.к. она удобнее и не такая тяжелая.
Строку 6 первого листинга нужно изменить так
<script type="text/javascript" src="<?php echo base_url(); ?>jquery-1.3.2.min.js"></script>
(естественно, предварительно нужно скачать библиотеку)
Код, который я привел в предыдущем комментарии должен находиться в файле scripts.js.
Остальной код в этом файле нужно немного переписать.
Вместо Ajax.Request использовать $.post(…). Пример использования есть на оф. сайте jQuery.
Помогите пожалуйста передать значение в функцию PHP.
Файл func.js
function probajs() {
var p = $('text').value;
var pars = $H({par:p}).toQueryString();
new Ajax.Request("http://ci_sample/proba/proba_ajax",
{method:"post", parameters:pars, onSuccess:parseResponse});
}
function parseResponse(param) {
var data = eval('(' + param.responseText + ')');
$('pajax').innerHTML = data.par;
}
Файл в контроллере proba.php
function proba_ajax() {
$par = ;// как правильно тут написать?
$res['par'] = $par*4;
echo json_encode($res);
}
Всё разобрался. Может кому пригодиться
$res['par'] = $this->input->post('par');