CodeIgniter и Ajax

Решил провести полный рефакторинг своей CMS (да-да, каждый вебразработчик просто обязан написать свою систему управления контентом), и заодно сделать панель управления удобнее.

Задача такая: есть дерево меню сайта в админке. Рядом с каждым пунктом меню — кнопка, нажав её, переходим на страницу редактирования свойств пункта меню: ссылки, активности, картинки. Задача следующая — не переходить на другую страницу, как в "суровые девяностые", а используя AJAX открыть тут же, в псевдо всплывающем окне, типа лайтбокса, который заполонил весь инет.

Начнём, как говорят "37 signals", с проектирования интерфейса. Использовать плагины и чужие скрипты не хочется, люблю, чтобы страницы загружались быстро. Поэтому обойдёмся без лайтбокса и jquery tools. Всплывающее окно будет у нас слоем с абсолютным позиционированием и следующими свойствами:

Copy Source | Copy HTML

  1. #divedit
  2. {
  3.      position: absolute;
  4.      z-index:0;
  5.      background: silver;
  6.      display: none;
  7.      top:50%;
  8.      left:50%;
  9.      width: 400px;
  10.      height: 274px;
  11.      margin-left: -200px;
  12.      margin-top: -137px;
  13.      border: gray solid;
  14. }

Copy Source | Copy HTML

  1. <div id="divedit">
  2.             <div class="box content">
  3.                 <form id="editform" name="editform" method="post" action="{base}cms/menu/edit">
  4.                     <input type="hidden" value="1" name="old_parent"/>
  5.                     <input type="hidden" value="2" name="lft"/>
  6.                     <label>Название: </label>
  7.                     <input type="text" style="width: 50%;" name="title" id="title"/>
  8.                     <br/>
  9.                     <br/>
  10.                     <label>Ссылка: </label>
  11.                     <input type="text" style="width: 50%;" value="" name="alias"/>
  12.                     <br/>
  13.                     <br/>
  14.                     <label>Родитель: </label>
  15.                     <div id="parent"></div>
  16.                     <br/>
  17.                     <br/>
  18.                     <label>Включён: </label>
  19.                     <div id="visible"></div>
  20.                     <br/>
  21.                     <br/>
  22.                     <label>Картинка: </label>
  23.                     <br/>
  24.                     <div id="picture" align="center"></div>
  25.                     <br/>
  26.                     <div align="center">
  27.                         <input class="subm i-accept" type="submit" value="Сохранить" name="submit_edit"/>
  28.                         <input class="subm i-cancel" type="button" value="Отмена" id="cancel_ed" name="cancel_ed"/>
  29.                     </div>
  30.                 </form>
  31.             </div>
  32.         </div>

Напишем функцию контроллера, которая должна будет передавать данные о пункте меню:

Copy Source | Copy HTML

  1. function ajaxitemdata()
  2. {
  3.     if ($this->input->post('id'))
  4.     {
  5.         $id = $this->input->post('id');
  6.         $this->MPTtree->set_table('menu_tree');
  7.         $node = $this->MPTtree->get_ORM($id);
  8.         $parent = $node->parent();
  9.         $arr = array(
  10.             'title' => $node->get('title'),
  11.             'alias' => $node->get('alias'),
  12.             'parent' => $this->MPTtree->display_as_select(1,$parent->get('lft'),'parent'),
  13.             'visible' => form_checkbox('visible', 'visible',$node->get('visible')),
  14.             'picture' => img($this->Menu_model->get_pic_by_id($node->get('id')))
  15.         );
  16.         echo json_encode($arr);
  17.     }
  18.     else
  19.     {
  20.         $arr = array('title' => 'Error');
  21.         echo json_encode($arr);
  22.     }
  23. }

В CodeIgniter для хранения деревьев я использую модель MPTtree, реализующую алгоритм Nested Sets. Надеюсь, вы в своих проектах не используете рекурсивное построение дерева сотней запросов в цикле? display_as_select — метод, выводящий сразу HTML код выпадающего списка со всеми пунктами меню, чтобы не парсить на стороне клиента каждую опцию. Результат выводится в формате JSON, легко читаемом и гораздо быстрее обрабатываемым, чем XML.

Следующий пример показывает JSON-представление объекта, описывающего человека. В объекте есть строковые поля имени и фамилии, объект, описывающий адрес, и массив, содержащий список телефонов.

{
   "firstName": "Иван",
   "lastName": "Иванов",
   "address": {
       "streetAddress": "Московское ш., 101, кв.101",
       "city": "Ленинград",
       "postalCode": 101101
   },
   "phoneNumbers": [
       "812 123-1234",
       "916 123-4567"
   ]
}

На языке XML подобная структура выглядела бы примерно так:

<person>
  <firstName>Иван</firstName>
  <lastName>Иванов</lastName>
  <address>
    <streetAddress>Московское ш., 101, кв.101</streetAddress>
    <city>Ленинград</city>
    <postalCode>101101</postalCode>
  </address>
  <phoneNumbers>
    <phoneNumber>812 123-1234</phoneNumber>
    <phoneNumber>916 123-4567</phoneNumber>
  </phoneNumbers>
</person>

http://ru.wikipedia.org/wiki/JSON

Теперь нужно вызвать эту функцию со стороны клиента:

Copy Source | Copy HTML

  1. $("button[name*='edit']").click(function() {
  2.                     $.post("{base}cms/menu/ajaxitemdata/", {id: this.value}, getitem, "json");
  3.                 });

При нажатии на кнопку, имя которой начинается с ‘edit’, отправляем POST запрос к серверу со значением id, равному value кнопки. Так же передаём имя callback функции (она выполнится после запроса и будет обрабатывать ответ) и указываем формат ответа — json.

Код callback функции:

Copy Source | Copy HTML

  1. function getitem(data)
  2.             {
  3.                 document.editform.title.value = data.title;
  4.                 document.editform.alias.value = data.alias;
  5.                 $("#parent").html(data.parent);
  6.                 $("#visible").html(data.visible);
  7.                 $("#picture").html(data.picture);
  8.                 $("#divedit").show("slow");
  9.             }

Значения полей ввода подставляем прямо в атрибут value элемента формы, для выпадающего списка, флажка и картинки — в пустые слои формы с соответствующими id подставляем html код. После этого открываем слой со спецэффектом.

Далее форма либо сабмитится, либо слой закрывается при нажатии на кнопку отмены. Для этого вешаем на неё обработчик:

Copy Source | Copy HTML

  1. $("#cancel_ed").click(function() {
  2.                     $("#divedit").hide("slow");
  3.                 });

Всё просто.