Сегодня мы разберем интересную и очень актуальную тему: взаимодействие Kohana 3.3.x и Ajax при помощи jQuery. Мы научимся отправлять данные из формы при помощи Ajax, делать валидацию данных, добавлять запись в БД, выводить обновленную информацию, удалять запись из Базы Данных – и все это будет без перезагрузки страницы.
Итак, сначала определим какие нам для этого понадобятся файлы:
- Контроллеры
- Articles - имеет 3 метода:
- Вывод списка статей
- Добавление новых статей, при удачном добавлении возвращает список статей (возвращает json обьект)
- Удаление статьи
- Articles - имеет 3 метода:
- Виды
- articles – список статей и форма добавления статьи
- main – наш базовый шаблон, в который мы будем вставлять все второстепенные виды
- Модель
- Articles – имеет 3 метода:
- Получение всех статей
- Добавление записи в БД
- Удалении записи из БД
- Articles – имеет 3 метода:
SQL
Для начала работы нам понадобится таблица articles в БД с данной структурой:
CREATE TABLE IF NOT EXISTS `articles` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `description` text NOT NULL, PRIMARY KEY (`id`) )
Views
Теперь сделаем наш главнй шаблон main.php, в нем бует костяк нашей страницы:
<!DOCTYPE html> <html lang="ru"> <head> <title>Ajax в Kohana 3.3.x</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0;"> <link href="/css/bootstrap.css" rel="stylesheet"> <link href="/css/bootstrap-responsive.css" rel="stylesheet"> <script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript"></script> <script src="/js/script.js" type="text/javascript"></script> </head> <body> <div id="wrap"> <div class="push"></div> <div class="container"> <div class="page-header"> <h1><a href="/">Ajax в Kohana 3.3.x</a></h1> </div> </div> <div class="container"> <?php print $content;?> </div> </div> </body> </html>
Теперь сделам шаблон для вывода списка новостей и форму добавления статьи articles.php:
<table class="table articles"> <thead> <th>ID</th> <th>Заголовок</th> <th>Текст</th> <th>Действия</th> </thead> <tbody> <?php if($data): ?> <?php foreach($data as $item): ?> <tr> <td><?php print $item['id']?></td> <td><?php print $item['name']?></td> <td><?php print $item['description']?></td> <td><a class="delete btn btn-danger" href="/articles/delete/<?php print $item['id']?>">Удалить</a></td> </tr> <?php endforeach; ?> <?php else: ?> <td colspan="4"><center>Добавьте статью</center></td> <?php endif; ?> </tbody> </table> <div class="alert alert-error" id="error" style="display:none"></div> <form action="/articles/add" method="POST" id="form_article"> <div class="form-item"> <label>Название</label> <input name="name" type="text" class="span5 input-medium " placeholder="Название статьи" /> </div> <div class="form-item"> <label>Текст</label> <textarea name="description" rows="5" class="span5" placeholder="Текст статьи"></textarea> </div> <div class="form-item"> <button type="submit" class="btn btn-primary">Добавить статью!</button> </div> </form>
jQuery
Также добавим обработчик на отправку формы, для валидации на стороне клиента и отправку данных посредтсвом ajax на сервер используя jQuery:
$("#form_article").submit(function(){ // при отправке формы проводим валидацию на заполненые поля и отправляем форму var form = $(this); $("[type=submit]",form).attr('disabled','disabled'); // делаем кнопку недоступной, чтобы избежать повторных нажатий $("#error").empty().hide(); // Очищаем блок с ошибкой и скрываем его var name = $('[name=name]',form).val(); // берем имя статьи из формы var description = $('[name=description]',form).val(); // берем текст из формы /** Если не заполнены поля - то выводим ошибку */ if(name == '' || description == ''){ $("#error").html('Ошибка валидации формы!').slideDown(); $("[type=submit]",form).removeAttr('disabled'); // делаем кнопку снова доступной return false; } $.ajax({ // описываем наш запрос type: "POST", // будем передавать данные через POST url: form.attr('action'), // берем адрес отправки формы и передаем туда наши данные аяксом data: form.serialize(), // серриализируем данные dataType: "json", // указываем, что нам вернется JSON beforeSend: function(){ $("#loading").slideDown(); // показываем индикатор загрузки }, success: function(data) { // когда получаем ответ if(!data.error){ // Если ошибки нет, то выводим список статей // Если есть статьи - то будем их выводить if(data.content){ var html = ''; for(var A in data.content){ var item = data.content[A]; html += '<tr><td>'+item.id+'</td><td>'+item.name+'</td><td>'+item.description+'</td><td><a class="delete btn btn-danger"href="/articles/delete/'+item.id+'">Удалить</a></td></tr>'; } $('table.articles tbody').html(html); } form.trigger('reset'); }else{ // Если сервер вернул ошибку то выводим текст ошибки $("#error").html(data.message).slideDown(); } /** Делаем кнопку отправки снова активной и убираем индикатор загрузки */ $("[type=submit]",form).removeAttr('disabled'); $("#loading").slideUp(); }, error: function(){ // Если сервер вернул ошибку, 4хх, 5хх /** Выводим ошибку, делаем кнопку отправки снова активной и убираем индикатор загрузки */ $("#error").html('Произошла ошибка').slideDown(); $("[type=submit]",form).removeAttr('disabled'); $("#loading").slideUp(); } }); return false; });
Добавим еще один обработчик события для удаления статей:
/** Функция удаления статьи */ $('body').on('click','.delete',function(){ var link = $(this); if(confirm('Вы действительно хотете удалить?')){ $.ajax({ type: "POST", url: link.attr('href'), // берем адрес из ссылки dataType: "json", success: function(data) { // когда получаем ответ if(!data.error){ // Если ошибки нет, то удаляем строку link.closest('tr').hide(function(){ $(this).remove(); }) }else{ // Если сервер вернул ошибку то выводим текст ошибки $("#error").html(data.message).slideDown(); } } }); } return false; });
Model
Создадим модель articles.php для работы с записями в БД:
<?php class Model_articles extends Model_Database { static $table_articles = "articles"; /** Выбор записей */ public function findBy() { return DB::select()->from(self::$table_articles)->execute()->as_array(); } /** Сохранение записи */ public function save($data){ if(!$data) return false; foreach($data as $k=>$v){ $key[] = $k; $value[] = $v; } $id = DB::insert(self::$table_articles, $key)->values($value)->execute(); return $id; } /** Удаление записи */ public function delete($id){ if($id){ return (bool) DB::delete(self::$table_articles)->where('id', '=', $id)->execute(); } return false; } }
Controller
И на последок создадим контролер, который будет обрабатывать наши запросы:
<?php defined('SYSPATH') or die('No direct script access.'); class Controller_Articles extends Controller_Template { public $template = "main"; public function action_index() { $data = array(); $modelArticles = new Model_articles; $data = $modelArticles->findBy(); // выбираем список статей из БД $this->template->content = View::factory('articles',array('data'=>$data)); // выводим список статей и форму для добавления статей } public function action_add() { $error = true; $id= null; $modelArticles = new Model_articles; if($post = $this->request->post()) { $data = array( 'name' => $post['name'], 'description' => $post['description'], ); $validate = Validation::factory($data); // готовимся к проведению валидации $validate -> rule(TRUE, 'not_empty'); // Проверяем на наличие пустых строк if($validate -> check()) // проводим валидацию { $id = $modelArticles->save($data); //добавляем в БД запись if ($id){ $error = false; } } } if (Request::initial()->is_ajax()){ // выполняем только если запрос был через Ajax if($error){ $result = array('error'=>true,'message'=>'Ошибка валидации формы!'); // по умолчанию возвращаем код с ошибкой }else{ // если валидация прошла успешно и запись в БД новой статьи прошла успешно $result['error'] = false; // возвращаем код успеха! $data = $modelArticles->findBy(); // выбираем список статей из БД и формируем масив if($data){ foreach($data as $v){ $result['content'][] = array( 'id' => $v['id'], 'name' => $v['name'], 'description' => $v['description'] ); } } } header('Content-Type: text/json; charset=utf-8'); // Устанавоиваем правильный заголовок echo json_encode($result); // на выходе отдаем код в формате JSON exit; }else{ $this->redirect('/articles'); // если запрос был не Аяксом, то редиректим на страницу списка статей } } public function action_delete() { $error = true; $modelArticles = new Model_articles; $id = (int) $this->request->param('id'); if($id) { $error = !$modelArticles->delete($id); //Удаляем статью } if (Request::initial()->is_ajax()){ // выполняем только если запрос был через Ajax if($error){ $result = array('error'=>true,'message'=>'Ошибка при удалении'); // по умолчанию возвращаем код с ошибкой }else{ $result['error'] = false; // возвращаем код успеха! } header('Content-Type: text/json; charset=utf-8'); // Устанавоиваем правильный заголовок echo json_encode($result); // на выходе отдаем код в формате JSON exit; }else{ $this->redirect('/articles'); // если запрос был не Аяксом, то редиректим на страницу списка статей } } }
Тут стоит отметить, что благодаря проверке Request::initial()->is_ajax() мы проверяем как нам передали данные, если передача была обычным методом - то мы просто редиректим на страницу со списком статей, если запросы был Аяксом, то выдаем список статей в JSON.
Готово, пробуем запустить http://localhost/articles/. Только в рабочем приложении не забудьте добавить дополнительные проверки входных данных, а также – авторизованный доступ.
Благодарю Вас за труды!
Очень помогло в освоении kohana.
Особенно версии 3.3 в русскоязычном пространстве.
Было бы здорово если бы Вы добавили в пример функцию редактирования статьи и запись ее в БД.
И еще хотелось бы увидеть реализацию этого примера через ORM.
Надеюсь, что мою просьбу не оставите без внимания.