Bug Tracker: создание страниц (часть шестая)

Владимир | | CodeIgniter, CSS, HTML, MySQL, PHP, Web разработка.

bug_tracker_logo_part6

Мы продолжаем разработку собственной системы отслеживания ошибок. И сегодня подробно рассмотрим создание страниц нашего приложения.

В предыдущих частях мы определились с типами страниц. Напомню, их всего два: страницы с общим перечнем багов (главная и страницы категорий) и страницы отдельных багов с комментариями.

Т.к. общее количество записей о багах может быть большим, мы будем использовать библиотеку pagination, входящую в состав фреймворка CodeIgniter, для вывода этого списка по частям.

Количество записей на одной странице мы задаем в файле конфигурации (application\config\config.php).

$config['bugs_per_page'] = 5;

Получить значение этого параметра можно так:

$this->config->item('bugs_per_page');

Теперь определимся с названиями методов контроллера и структурой URL.

По-умолчанию, URL в CodeIgniter имеют такой вид:

sitename.domen/index.php/имя_контроллера/имя_метода/параметр1/параметр2/…

Контроллер у нас будет называться bugtracker, а метод, показывающий страницы с багами – page. В результате получим URL с такой структурой:

…/bugtracker/page/номер_записи

В последнем сегменте адреса указываем номер первой записи на текущей странице. Кстати, это не id бага в базе данных, это его индекс в массиве с результатами поиска по БД.

Создаём контроллер (application\controllers\bugtracker.php)

class BugTracker extends Controller {

//настройки отображения списка багов private $listConf = array( 'commentOpen'=>'<li class="depth-{depth}">' ); function BugTracker() { parent::Controller(); $this->load->model('mbug'); $this->load->model('mcategory'); $this->load->model('mcomment'); $this->load->library('Table2Tree'); $this->load->library('session'); $this->load->helper('form'); } function index() { $this->page(); } function page($firstBug = 0) { ... } }

Как видите, на данный момент он содержит конструктор, в котором мы загрузили модели и несколько библиотек, а также два метода.

1) index – вызывается CodeIgniter’ом если имя метода явно не указано в URL;

2) page($firstBug = 0) – показывает записи о багах начиная с $firstBug.

Т.к. на главной странице у нас будет отображаться список багов начиная с первого, то из метода index мы просто вызываем page без параметров.

Теперь подробно рассмотрим метод page.

function page($firstBug = 0) {
	if (!is_integer((int)$firstBug)) {
		$firstBug = 0;
	}
	
	//сохраняем адрес этой страницы в сессии
	$this->session->set_userdata(array('prev_page'=>current_url()));
	
	$pageData['title'] = 'Bug Tracker';

	//настраиваем разбивку на страницы
	$this->load->library('pagination');
	$pconf['base_url'] = $this->config->item('base_url').'bugtracker/page';
	$pconf['total_rows'] = $this->mbug->getBugsCount();
	$pconf['per_page'] = $this->config->item('bugs_per_page');
	
	$this->pagination->initialize($pconf);
	$pageData['paginationLinks'] = $this->pagination->create_links();

	//загружаем общий список ошибок и комментариев к ним
	$bugs = $this->mbug->getAllBugs($firstBug, $this->config->item('bugs_per_page'));
	
	if ($bugs !== false) {
		$bugsTree = $this->table2tree->getTree($bugs, $firstBug, $this->config->item('bugs_per_page'));
		
		$pageData['bugsList'] = $this->table2tree->getHTMLList($bugsTree, $this->listConf);
		
	}

	$pageData['categories'] = $this->mcategory->getAllCategories();
	
	$this->load->view('header', $pageData);
	$this->load->view('categories');
	//размещаем форму отправки сообщений о багах
	$this->load->view('addbugform');
	$this->load->view('allbugs');
	$this->load->view('footer');
}

Его работу можно разделить на следующие этапы.

1) Проверяем параметр и сохраняем адрес текущей страницы в сессии (зачем это нужно я расскажу немного позже).

2) Настраиваем разбивку на страницы (строки 11-18). Для этого загружаем библиотеку pagination, передаём ей массив с параметрами и создаем ссылки. В качестве параметров мы указываем: общее количество записей (получаем с помощью метода getBugsCount()), количество записей, которые нужно показать на странице (читаем из конфига) и первую часть URL ссылок (к нему последним параметром библиотека будет добавлять номер первой записи на очередной странице).

Т.е. если мы показываем по 5 записей на странице, библиотека создаст такие URL:
.../bugtracker/page/
.../bugtracker/page/5
.../bugtracker/page/10

После этого мы загружаем список багов для данной страницы (строка 21). Запрос, который формирует этот список мы рассматривали в прошлый раз.

3) С помощью библиотеки table2tree мы преобразуем таблицу с данными о багах в HTML список (строки 23-28).

4) Загружаем список категорий (строка 30). Он используется для создания навигации по баг трекеру.

5) Показываем страницы (строки 32-37). Для этого загружаем представления и передаем им параметры.

Подробно рассматривать все представления смысла я не вижу. В любом случае оформлением я не занимался, поэтому, скорее всего, их придется немного изменить. Например, добавить CSS классы. Но на принцип работы и передаваемые данные это влиять не будет.

Поэтому сейчас я просто приведу краткое описание всех представлений. Все они находятся в папке (application\views):

header.php – формирует заголовок страницы, загружает JavaScript и CSS файлы;
categories.php – формирует список с перечнем категорий (используется для навигации);
addbugform.php – форма добавления записи о найденном баге;
allbugs.php – показывает список багов и строку со ссылками на другие страницы;
footer.php – «хвостовик» страницы.

Создание страницы выбранной категории.

Приводить код этого метода я не буду, т.к. он практически полностью повторяет метод page. Тем не менее, есть несколько нюансов на которых стоит остановиться.

Во-первых, метод контроллера, создающий страницу отдельной категории, называется category и принимает 2 параметра: название категории и номер первой записи на странице.

Т.е. URL имеет такой вид:

…/bugtracker/category/имя_категоии/номер_записи

Во-вторых, при настройке библиотеки pagination необходимо явно указать в каком сегменте URL находится номер первой записи (по-умолчанию, это значение равно 3).

$pconf['uri_segment'] = 4;

В третьих, список багов получаем с помощью метода getBugsByCategory (модель mbugs). В прошлый раз мы рассматривали SQL запрос, который возвращает список багов для указанной категории.

Остальной код полностью повторяет метод page.

Создание страницы отдельного бага (метод bug).

function bug($id = 1) {
	if (!is_integer((int)$id) || (int)$id <= 0) {
		redirect('bugtracker/index');
		return;
	}
	
	//сохраняем адрес этой страницы в сессии
	$this->session->set_userdata(array('prev_page'=>current_url()));
	
	//ищем указанный баг
	$bug = $this->mbug->getBug($id);
	if ($bug !== false) {
		$bugTree = $this->table2tree->getTree($bug);
		
		$pageData['bugList'] = $this->table2tree->getHTMLList($bugTree, $this->listConf);
		$pageData['bug_id'] = $bugTree[0]['id'];
	}
	
	$pageData['categories'] = $this->mcategory->getAllCategories();
	
	$this->load->view('header', $pageData);
	$this->load->view('categories');
	$this->load->view('onebug');
	$this->load->view('addcommentform');
	$this->load->view('footer');
}

Этот метод очень похож на предыдущие. Но есть несколько существенных различий.

1) Здесь мы не используем библиотеку pagination. В ней просто нет смысла. На странице отображается запись только об одном баге и все комментарии к нему.

2) Данные бага загружаем с помощью метода getHTMLList (строка 15). Кстати, для создания дерева комментариев мы используем собственную библиотеку table2tree. Её методы getTree и getHTMLList автоматически определяют есть ли в исходных данных комментарии и глубину их вложенности.

3) Вместо формы добавления бага вставляем форму добавления комментария (строка 24).

На этом мы закончим эту часть.
В следующий раз рассмотрим добавление записей и комментариев.

До встречи!

  • Big_Shark

    я бы объедение «Создание страницы выбранной категории.» и Главной страницы

    • Можно, но код при этом немного усложняется и URL'ы будут выглядеть хуже. Т.к. придется передавать тип страницы в одном из параметров и анализировать его.

  • Big_Shark

    я бы объедение «Создание страницы выбранной категории.» и Главной страницы

    • Можно, но код при этом немного усложняется и URL'ы будут выглядеть хуже. Т.к. придется передавать тип страницы в одном из параметров и анализировать его.

  • Маленький хинт,
    $config['bugs_per_page'] = 5;
    $this->config->item('bugs_per_page');
    $pageData['title'] = 'Bug Tracker'; и т.д.
    это не хорошо, очепятка в индексе может привести к скрытой ошибке, советую всегда использовать константы.
    $config[BUGS_PER_PAGE] = 5;
    $this->config->item(BUGS_PER_PAGE);
    $pageData[TITLE] = 'Bug Tracker';

    • Т.е. предварительно объявить все эти константы?
      define(«BUGS_PER_PAGE», «bugs_per_page»);

      Большое спасибо за этот совет. Иногда почему-то не обращаешь внимание на очевидные вещи 🙂

      И кроме опечаток есть еще одно преимущество вашего метода. В Eclipse работает автодополнение для констант (в большинстве других IDE тоже будет работать).

      • zash

        Не вижу преимуществ. В большинстве даже не IDE а простых редакторов есть автодополнение переменных так что вероятность опечаток ничтожно мала. А если проект со временем разростется и мне нужно будет например на какой-то странице вывести не 5 а 10 багов? И еще один минус в том что не хочется терять время на такие вещи как думать о будущем, возможен ли такой вариант когда надо будет менять переменную, а также писать лишние строки кода которые никому не нужны.

        • Автодополнение в данном случае не работает, т.к. речь идет о ключе массива в виде текстовой строки.
          Точнее автодополнение будет работать в варианте AmdY, т.к. он использует константы.

          Если на разных страницах должно быть разное количество багов, то это количество придется хранить в базе, а не файле конфигурации.

    • Big_Shark

      а по мне так это бред

      • Почему? Скорость ввода увеличивается и механических ошибок будет меньше. Особенно если данные из конфига часто приходится «вручную» читать.

        • Big_Shark

          Я очень часто использую массивы и эти массивы достигают гигантских размеров и чтож мне делать описывать сотни индексов?
          В массивах огромный плюс это генерация ключей которой я обычно и пользуюсь.
          Единственное когда я вижу их применения это когда есть ключу дают определенное сложное названия для того чтобы его пользователь случайно не подменил и это сложное названия записывают в define переменную которую в последствии и будет использовать пользователь для у прошения ввода.

          Скорость ввода увеличивается и механических ошибок будет меньше.
          Скорость ввода и увеличивается не на много так как нужно все define вначале объявить.

          Особенно если данные из конфига часто приходится «вручную» читать.

          Я обычно подгружаю конфиг файл для данного контролера(модуля) и его содержимое сохраняю в переменной $this->cfg=array();
          И я не имею понятия какие там переменные есть и с какими названиями мне главное чтобы те которые я использовал небыли заменены.

          Я бы хотел посмотреть на реальное предложения где использую
          define для запоминания ключей массивов.

        • В массивах огромный плюс это генерация ключей которой я обычно и пользуюсь.

          В этом случае, конечно, никакого смысла в константах нет.
          Но если ключей <10 и созданы они не автоматически, то можно и константы использовать.

          Я бы хотел посмотреть на реальное предложения

          Честно говоря, не знаю таких 😉

  • Маленький хинт,
    $config['bugs_per_page'] = 5;
    $this->config->item('bugs_per_page');
    $pageData['title'] = 'Bug Tracker'; и т.д.
    это не хорошо, очепятка в индексе может привести к скрытой ошибке, советую всегда использовать константы.
    $config[BUGS_PER_PAGE] = 5;
    $this->config->item(BUGS_PER_PAGE);
    $pageData[TITLE] = 'Bug Tracker';

    • Т.е. предварительно объявить все эти константы?
      define(«BUGS_PER_PAGE», «bugs_per_page»);

      Большое спасибо за этот совет. Иногда почему-то не обращаешь внимание на очевидные вещи 🙂

      И кроме опечаток есть еще одно преимущество вашего метода. В Eclipse работает автодополнение для констант (в большинстве других IDE тоже будет работать).

      • zash

        Не вижу преимуществ. В большинстве даже не IDE а простых редакторов есть автодополнение переменных так что вероятность опечаток ничтожно мала. А если проект со временем разростется и мне нужно будет например на какой-то странице вывести не 5 а 10 багов? И еще один минус в том что не хочется терять время на такие вещи как думать о будущем, возможен ли такой вариант когда надо будет менять переменную, а также писать лишние строки кода которые никому не нужны.

        • Автодополнение в данном случае не работает, т.к. речь идет о ключе массива в виде текстовой строки.
          Точнее автодополнение будет работать в варианте AmdY, т.к. он использует константы.

          Если на разных страницах должно быть разное количество багов, то это количество придется хранить в базе, а не файле конфигурации.

    • Big_Shark

      а по мне так это бред

      • Почему? Скорость ввода увеличивается и механических ошибок будет меньше. Особенно если данные из конфига часто приходится «вручную» читать.

        • Big_Shark

          Я очень часто использую массивы и эти массивы достигают гигантских размеров и чтож мне делать описывать сотни индексов?
          В массивах огромный плюс это генерация ключей которой я обычно и пользуюсь.
          Единственное когда я вижу их применения это когда есть ключу дают определенное сложное названия для того чтобы его пользователь случайно не подменил и это сложное названия записывают в define переменную которую в последствии и будет использовать пользователь для у прошения ввода.

          Скорость ввода увеличивается и механических ошибок будет меньше.
          Скорость ввода и увеличивается не на много так как нужно все define вначале объявить.

          Особенно если данные из конфига часто приходится «вручную» читать.

          Я обычно подгружаю конфиг файл для данного контролера(модуля) и его содержимое сохраняю в переменной $this->cfg=array();
          И я не имею понятия какие там переменные есть и с какими названиями мне главное чтобы те которые я использовал небыли заменены.

          Я бы хотел посмотреть на реальное предложения где использую
          define для запоминания ключей массивов.

        • В массивах огромный плюс это генерация ключей которой я обычно и пользуюсь.

          В этом случае, конечно, никакого смысла в константах нет.
          Но если ключей <10 и созданы они не автоматически, то можно и константы использовать.

          Я бы хотел посмотреть на реальное предложения

          Честно говоря, не знаю таких 😉

  • Я не совсем понял сам процесс работы.

  • Я не совсем понял сам процесс работы.

  • Олег

    Было бы еще неплохо в каждой статье цикла выкладывать полный pack исходников. Тогда можно будет легко отследить прогрее.

    Или же воспользоваться например SVN от Google Code) У тебя же не проприетарный баг трекер будет :), и версию для конкретного урока складывать в tags.

    • Нет, баг трекер, конечно, не проприетарный 😉
      Но исходники я выкладываю только для более-менее работающих вариантов. Например, на данный момент последние исходники выложены здесь.
      Баг трекер работает, но пока без AJAX.

      Промежуточные варианты я выкладывать не хочу, т.к. они зачастую не полностью совпадают с постами. Поэтому от них будет больше путаницы чем пользы.

  • Олег

    Было бы еще неплохо в каждой статье цикла выкладывать полный pack исходников. Тогда можно будет легко отследить прогрее.

    Или же воспользоваться например SVN от Google Code) У тебя же не проприетарный баг трекер будет :), и версию для конкретного урока складывать в tags.

    • Нет, баг трекер, конечно, не проприетарный 😉
      Но исходники я выкладываю только для более-менее работающих вариантов. Например, на данный момент последние исходники выложены здесь.
      Баг трекер работает, но пока без AJAX.

      Промежуточные варианты я выкладывать не хочу, т.к. они зачастую не полностью совпадают с постами. Поэтому от них будет больше путаницы чем пользы.