В комментариях к прошлой части я получил много советов о том, в каком виде лучше получать данные из базы. Я очень признателен всем за эти замечания, т.к. они действительно помогли найти недостатки в прошлой части.
Основная проблема была в том, что я не продумал до конца, что именно будет отображаться на страницах баг трекера, но взялся за работу с базой.
В общем-то, страниц у баг трекера не много. Первоначально я вообще хотел сделать одностраничное приложение, но передумал из-за проблем с индексацией поисковиками.
Поэтому сегодня постараюсь максимально подробно осветить этот вопрос.
Т.к. индексация страниц баг трекера поисковиками очень желательна, то при разработке имеет смысл придерживаться рекомендаций от Google, а именно методики Progressive Enhancement (постепенного улучшения).
Идея следующая. Нужно сделать приложения так, чтобы без поддержки JavaScript (только с помощью обычных ссылок) можно было получить доступ к любой информации (багам и комментариям).
После этого, с помощью JavaScript вносятся различные улучшения. Например, без JavaScript при клике на заголовке бага посетитель попадет на страницу с его описанием и комментариями.
Если JavaScript включен, то после загрузки страницы обычные ссылки будут преобразованы в ajax-ссылки и вместо перехода на другую страницу будет подгужен список комментариев.
Кроме того, не правильно показывать на одной странице одновременно несколько багов вместе с комментариями (как я планировал раньше). Во-первых, если комментарии содержат рисунки, то такая страница будет долго грузиться. Во-вторых, получится, что отдельные страницы багов будут дублировать главную, а, насколько я знаю, поисковики это не любят (дублирование контента).
В результате получается, что для работы баг трекера нужны страницы двух типов.
1) Страницы с общим перечнем багов. Они будут содержать заголовок и описание бага (без комментариев). Сюда относятся: главная и страницы категорий. Тут же будет форма для добавления сообщения о новом баге.
2) Страницы отдельных багов. Тут будет полная информация о баге и дерево комментариев. Плюс форма добавления комментария.
Кстати, из страниц первого типа можно будет сформировать RSS ленты (как предлагал Алексей Качаев).
Теперь посмотрим, что нам нужно сделать для того, чтобы сформировать содержимое этих страниц.
Как ни странно, но практически вся работа сделана в прошлой части 😉 .
У нас есть библиотека, которая позволяет преобразовывать таблицу с багами и комментариями в html списки. При этом совершенно не важно, сколько багов и комментариев находится в этой таблице.
Например, если мы получим из базы таблицу только с информацией о багах (без комментариев), то сможем точно также сформировать html список.
Кроме того, у нас есть шаблоны для отображения багов и комментариев. И совершенно неважно как их использовать, для создания списка багов с комментариями или без них.
Тем не менее, в библиотеку нужно внести несколько мелких исправлений, чтобы сделать её более универсальной.
1) Убрать array_slice
. Дело в том, что я сразу не разобрался как ограничить количество результатов, которые возвращает SQL запрос при использовании объединений (JOINS), но потом исправил эту ошибку.
2) Добавить метод getHTMLCommentsList
, который будет использоваться для преобразования массива с комментариями (без данных о баге) в HTML список. Этот метод потребуется при загрузке списка комментариев с помощью AJAX.
function getHTMLCommentsList($comments, $config = null) { if (!empty($config)) { foreach ($config as $key => $value) { $this->config[$key] = $value; } } $this->CI->load->library('parser'); $html = $this->_convert2HTMLList($comments, 0); return $html; }
Этот метод по сути представляет собой оболочку, для _convert2HTMLList
, описанного в прошлой части.
3) Добавить проверку наличия полей с данными о комментариях (в методе _convert2SimpleTree
). Т.к. теперь этот метод может получить данные багов как с комментариями так и без них.
Исходники библиотеки можно скачать в виде архива. Ссылка в конце страницы.
Кроме того, я думаю, не стоит отказываться от возможности получения полного списка багов с комментариями. По крайней мере, есть один случай, в котором это может быть полезно. Речь о том, что кроме RSS можно организовать отправку сообщений о новых багах на eMail, например, раз в день. И в таких письмах имеет смысл отправить не только описания багов, но и комментарии к ним. Ведь если между добавлением бага и отправкой письма может пройти несколько часов, то, скорее всего и комментарии появятся.
Также, думаю, стоит рассмотреть основные SQL запросы, с помощью которых можно будет получить информацию для перечисленных выше страниц. Диаграмма со структурой таблиц приведена в третьей части.
1) Получение списка багов (используется для создания главной страницы и страниц категорий).
SELECT b.* , c.link, c.name AS category FROM bugs AS b LEFT JOIN categories AS c ON b.category_id = c.id ORDER BY b.bug_date DESC LIMIT ?, ?
Обратите внимание на LIMIT в конце запроса. С его помощью мы ограничиваем число багов на странице. Естественно, при этом будет использоваться библиотека пагинации из CodeIgniter.
2) Получение списка багов для выбранной категории.
SELECT COUNT(b.id) AS catsnum FROM bugs AS b LEFT JOIN categories AS c ON b.category_id = c.id WHERE c.link=?
3) Получение подробной информации о баге и всех его комментариев.
SELECT b.*, c.link, c.name AS category, cm.id AS c_id, cm.uname AS c_uname, cm.description AS c_description, cm.comment_date, cm.parent_id, cm.bug_id, cm.uemail AS c_uemail FROM bugs AS b LEFT JOIN categories AS c ON b.category_id=c.id LEFT JOIN comments AS cm ON b.id=cm.bug_id WHERE b.id=?
4) Получение отдельно списка комментариев выбранного бага (для работы в ajax-режиме). По-моему, не имеет смысла использовать отдельный запрос для решения этой задачи. Ведь все необходимые данные можно получить предыдущим запросом.
5) Получение списка багов и комментариев (я приводил этот запрос в прошлый раз, но сейчас немного изменил его и ввел ограничение на количество полей).
SELECT b.* , c.link, c.name AS category, cm.id AS c_id, cm.uname AS c_uname, cm.description AS c_description, cm.comment_date, cm.parent_id, cm.bug_id, cm.uemail AS c_uemail FROM comments AS cm RIGHT JOIN ( SELECT * FROM bugs ORDER BY bugs.bug_date DESC LIMIT ?, ? ) b ON b.id = cm.bug_id LEFT JOIN categories AS c ON b.category_id = c.id
Тут стоит обратить внимание на строку 7.
6) Получение общего количества багов (используется для пагинации)
function getBugsCount() { return $this->db->count_all('bugs'); }
Это не SQL запрос 😉 Здесь я использовал встроенную функцию CodeIgniter. На самом деле выполняется
SELECT COUNT(*) AS 'numrows' FROM 'bugs'
7) Получение общего количества багов в выбранной категории
SELECT COUNT(b.id) AS catsnum FROM bugs AS b LEFT JOIN categories AS c ON b.category_id = c.id WHERE c.link=?
Я не буду подробно описывать принцип работы каждого запроса. В прошлой части я довольно подробно останавливался на одном из самых сложных (пятый запрос в этом списке). Самое главное четко представлять как работают объединения таблиц (JOINS).
Вместо вопросительно знака в запросы будут подставлены конкретные данные.
В завершение приведу ссылку на ахив с исходниками библиотеки table2tree.
Как видите, изменений минимум. На этом примере очень хорошо видно одно из основных преимуществ архитектуры MVC. За счет того, что вся работа с базой данных вынесена в модель можно расширять возможности приложения практически без изменений в остальной части кода.
До встречи!
P.S. Как всегда я рад выслушать любые замечания и предложения.