Лекция 7. Модули. Постраничный вывод информации. Навигационная цепочка
Модули
Как уже говорилось, модули в Joomla используются для отображения небольших фрагментов контента, обычно в левой или правой колонке или верхней или нижней областях страницы. Типичный модуль выводит информацию из таблицы какого-нибудь компонента, например, случайную фотографию или несколько последних статей.
По сравнению с разработкой компонента написать модуль значительно легче. Как правило, модуль не использует собственных таблиц и не обрабатывает данные, введенные пользователем. Код модуля может даже поместиться в одном-единственном файле.
Постраничный вывод информации (класс JPagination)
Joomla позволяет разбивать длинные списки на страницы, задавая длину списка по умолчанию ("Сайт" - "Общие настройки", выпадающий список "Длина списка по умолчанию"). Для вывода списков элементов с разбивкой на страницы как в бэкенде, и так и во фронтенде используется класс JPagination. Его открытые (public) поля хранят следующую информацию:
total | - общее количество записей; |
limitstart | - порядковый номер записи, с которой нужно начать вывод; |
limit | - количество записей на страницу; |
prefix | - префикс переменных запроса. |
Соответственно, конструктор принимает эти четыре значения в качестве параметров:
__construct(int $total, int $limitstart, int $limit, string $prefix = '')
Например:
$paginationObject = new JPagination(100, 20, 10, 'somePrefix');
Как вы, возможно, помните, второй и третий параметры метода JDatabase::setQuery() - это смещение для начала выборки и количество выбираемых строк. Они в точности соответствуют параметрам limitstart и limit. Поэтому используйте одни и те же значения для создания объекта JPagination и для задания параметров setQuery():
$db->setQuery("SELECT * FROM #__mycomponent", $limitstart, $limit); $rows = $db->loadObjectList(); jimport('joomla.html.pagination'); $paginationObject = new JPagination($total, $limitstart, $limit);
После создания объекта JPagination необходимо вызвать один из его методов для отображения счетчика страниц, ссылки на предыдущую/следующую страницу и т.д. Все эти методы не имеют параметров и возвращают HTML-код соответствующих элементов. Для наглядности посмотрите на результаты работы этих методов ( таблица 7.1).
Метод | Результат |
---|---|
getPagesCounter() | Рис. 7.1. Результат работы метода getPagesCounter() |
getResultsCounter() | Рис. 7.2. Результат работы метода getResultCounter() |
getPagesLinks() | Рис. 7.3. Результат работы метода getPagesLinks() |
getListFooter() | Рис. 7.4. Результат работы метода getListFooter() |
getLimitBox() | Рис. 7.5. Результат работы метода getLimitBox() |
Например:
echo $paginationObject->getListFooter();
Также класс JPagination содержит методы orderUpIcon() и orderDownIcon() для вывода стрелок "вверх" и "вниз", использующихся для задания собственного порядка записей.
Управление навигационной цепочкой (класс JPathway)
Навигационная цепочка ("хлебные крошки") - это последовательность элементов, представляющая собой путь по сайту от корня до текущей страницы. Для управления навигационной цепочкой в Joomla существует объект JPathway, доступ к которому можно получить так:
global $app; $pathway =& $app->getPathway();
Добавление элемента в навигационную цепочку
bool addItem(string $name, string $link='')
где
$name | - название (текст) элемента; |
$link | - гиперссылка. |
Например:
$pathway->addItem('Категория #1','mycomponent/category/1'); $pathway->addItem('Элемент #1');
Получившаяся цепочка показана на рис. 7.6.
Рис. 7.6. Навигационная цепочка
Обратите внимание, что для элемента, который окажется в цепочке последним, ссылка выводиться не будет, даже если она задана. Это понятно, т.к. последний элемент соответствует текущей странице, ссылка на которую и без того известна. Тем не менее, такая ссылка не теряется и сохраняется в массиве _pathway, в котором класс JPathway хранит элементы цепочки как объекты с двумя полями - name и link.
Получение массива элементов навигационной цепочки
array getPathway()
Для приведенного выше примера массив выглядит так:
Array ([0]=>stdClass Object ([name]=>Категория #1 [link]=>mycomponent/category/1) [1]=>stdClass Object ([name]=>Элемент #1 [link]=>))
Получение только названий элементов без ссылок
array getPathwayNames()
Для того же примера этот метод вернет массив
Array ([0]=>[1]=>Категория #1 [2]=>Элемент #1)
Изменение названия заданного элемента
bool setItemName(int $id, string $name)
где
$id | - индекс элемента; |
$name | - новое название. |
Пример:
$pathway->setItemName(0,'Категория #2');
Вид навигационной цепочки после выполнения этого кода показан на рис. 7.7.
Рис. 7.7. Измененная навигационная цепочка
Замена массива элементов навигационной цепочки
array setPathway(array $pathway)
где $pathway - новый массив объектов цепочки (т.е. для каждого элемента должны быть заданы поля name и link).
Метод возвращает предыдущее значение массива.
Например:
$item1->name = "item1"; $item1->link = "link1"; $item2->name = "item2"; $item2->link = "link2"; $item3->name = "item3"; $item3->link = ""; $items = array($item1, $item2, $item3); $pathway->setPathWay($items);
Получившаяся цепочка показана на рис. 7.8.
Рис. 7.8. Навигационная цепочка после замены массива ее элементов
Практика
Разработка модуля
Регистрация модуля в базе данных
Модуль, как и компонент, необходимо зарегистрировать в базе данных. Для этого создадим запись в таблицах #__modules и #__extensions. Выполните следующие SQL-запросы:
INSERT INTO jos_modules (title, ordering, position, published, module, showtitle, params) VALUES ('Новые вопросы', 1, 'position-7', 1, 'mod_myquestions', 1, '{"random":"0","items":"3","maxlen": "100","author":"1","date":"1"}'); INSERT INTO jos_extensions(name,type,element,folder,client_id,manifest_cache,params,custom_data,system_data) VALUES('mod_myquestions', 'module', 'mod_myquestions', '', 0, '{"legacy":false,"name":"mod_myquestions", "type":"module","creationDate":"2012","author":"Me", "copyright":"","authorEmail":"","authorUrl":"","version":"1.6.0", "description":"My Questions module","group":""}', '{}', '', '');
Если вы обновите фронтенд после выполнения этих запросов, вы заметите, что модуль не появился, несмотря даже на то, что полю published присвоено значение 1. Дело в том, что модуль должен быть не только опубликован, но еще и назначен для каких-либо пунктов меню. Для этого в панели администрирования войдите в меню "Расширения" - "Менеджер модулей" и выберите модуль "Вопросы". В разделе "Привязка к пунктам меню" выберите в выпадающем списке значение "На всех страницах" и нажмите кнопку "Сохранить и закрыть" ( рис. 7.9).
Рис. 7.9. Привязка модуля ко всем страницам
Создание модуля
Напишем модуль, который будет выводить ссылки на последние вопросы или на случайный вопрос. Создадим в папке /modules папку mod_myquestions, а в ней - файл mod_myquestions.php:
<?php defined('_JEXEC') or die ('Restricted access'); $items = $params->get('items', 1); $maxlen = $params->get('maxlen',100); $random = $params->get('random', 0); $q_author = ($params->get('author',1) == 1) ? ", name" : ""; $q_date = ($params->get('date',1) == 1) ? ", date" : ""; $db = &JFactory::getDbo(); $query = "SELECT id, question$q_author$q_date FROM #__myquestions WHERE answer <> '' AND (published = 1 OR (expiration_date <> '0000-00-00 00:00:00' AND expiration_date > NOW()))"; if ($random) { $orderby = " ORDER BY RAND()"; $items = 1; } else $orderby = " ORDER BY date DESC"; $query .= $orderby; $db->setQuery($query, 0, $items); $rows = $db->loadObjectList(); foreach($rows as $row) { echo '<a href="'.JRoute::_("index.php?option=com_myquestions&view=question&task=show& id=".$row->id).'">'.substr(strip_tags($row->question),0,$maxlen-1).'</a><br/>'; $addition = array(); if ($params->get('author',1) == 1) $addition[] = $row->name; if ($params->get('date',1) == 1) $addition[] = JHTML::_('date', $row->date, JText::_('DATE_FORMAT_LC3')); if (count($addition)) echo '<i>'.implode(' ', $addition).'</i><br/>'; } ?>
Для установки и считывания параметров может быть использован глобальный объект $params. Когда мы добавили запись в таблицу #__modules, поле params содержало пять значений: random, равное 0, items, равное 3, maxlen, равное 100, author, равное 1, date, равное 1. Их значения мы получаем с помощью метода get(), второй параметр которого - это значение по умолчанию.
Далее задается SQL-запрос для получения из таблицы #__myquestions вопросов, подлежащих публикации. Значения id и question мы получаем в любом случае, а name и date - если в настройках модуля задано, что нужно выводить эти значения. Если нужно выводить один случайный вопрос, то к запросу добавляется "ORDER BY RAND()" и количество записей, которое мы хотим получить, вне зависимости от значения items, заданного в настройках, задается равным 1. В противном случае нужно выводить несколько самых новых вопросов, поэтому к запросу добавляется "ORDER BY date DESC" для сортировки по дате вопроса.
Второй и третий параметры функции setQuery() используются для задания оператора LIMIT, который автоматически добавится к запросу. Таким путем мы получаем из базы данных только первые items записей.
Для каждой полученной записи выводится ссылка на ее просмотр с помощью JRoute::_() и первые maxlen символов, а также, если это разрешено в настройках, имя автора и дата.
Теперь во фронтенде появится модуль, показанный на рис. 7.10. Если модуль не отображается, попробуйте изменить позицию с position-7 на какую-либо другую.
Рис. 7.10. Модуль, отображающий три самых новых вопроса
С помощью phpMyAdmin измените значение random в настройках модуля mod_myquestions (поле params таблицы #__modules) на "1":
{"random":"1","items":"3","maxlen":"100","author":"1","date":"1"}
Теперь модуль выводит один случайный вопрос ( рис. 7.11).
Рис. 7.11. Модуль, отображающий один случайный вопрос
Постраничный вывод информации
Добавим к менеджеру вопросов постраничный вывод списка. Откройте файл /administrator/components/com_myquestions/controller.php и сделайте изменения в коде функции showQuestions() в соответствии с выделенным кодом:
function showQuestions() { $option = JRequest::getVar('option'); global $app; $limit = JRequest::getVar('limit', $app->getCfg('list_limit')); $limitstart = JRequest::getVar('limitstart', 0); $db =& JFactory::getDbo(); $query = "SELECT count(*) FROM #__myquestions"; $db->setQuery($query); $total = $db->loadResult(); $query = "SELECT * FROM #__myquestions"; $db->setQuery($query, $limitstart, $limit); $rows = $db->loadObjectList(); if ($db->getErrorNum()) { echo $db->stderr(); return false; } jimport('joomla.html.pagination'); $pageNav = new JPagination($total, $limitstart, $limit); HTML_questions::showQuestions($option, $rows, $pageNav); }
Значения limit и limitstart мы получаем из HTTP-запроса. Их задает один из методов класса JPagination, который мы вызовем далее. По умолчанию limit берется из настроек Joomla, а limitstart принимается равным 0. Значение total мы получаем из базы данных.
Когда все данные получены, создается объект JPagination, который передается в метод HTML_questions::showQuestions().
Откройте файл admin.myquestions.html.php и измените метод showQuestions() следующим образом:
function showQuestions($option, &$rows, &$pageNav) { $maxlen = 100; ?> <form action="index.php" method="post" name="adminForm"> <table class="adminlist"> ... <?php jimport('joomla.filter.output'); $k = 0; for ($i = 0, $n = count($rows); $i < $n; $i ++) { ... } ?> <tfoot> <td colspan="10"> <?php echo $pageNav->getListFooter();?> </td> </tfoot> </table> ... </form> <?php }
Код, полученный с помощью getListFooter(), выведет выпадающий список для выбора количества элементов на странице и ссылки на другие страницы. Название элемента <select> - limit, таким образом, выбранное в выпадающем списке значение попадет в HTTP-запрос как значение переменной limit. Также getListFooter() добавит к форме скрытое поле limitstart, значение которого будет изменяться в зависимости от limit и от нажатой пользователем ссылки на страницу: 0 для первой страницы, limit для второй, 2*limit для третьей и т.д. Таким образом переменная limitstart также будет включена в HTTP-запрос. Из него при переходе на другую страницу эти значения получит функция showQuestions() и использует их при задании нового запроса к базе данных.
Для проверки задайте в настройках сайта длину списка по умолчанию, равную 5, и добавьте вопросы так, чтобы их количество стало больше, чем 5. Примерный вид, который теперь примет список вопросов в бэкенде, показан на рис. 7.12.
Рис. 7.12. Список вопросов с разбиением на страницы
Нацигационная цепочка
Рассмотрим, какие элементы можно отобразить в навигационной цепочке для каждого из представлений нашего компонента:
all | - название компонента; | |
category | - название компонента, название категории; | |
question | шаблон default | - название компонента, название категории, текст вопроса; |
шаблон default_form | - название компонента, текст "Задать вопрос". |
Как видите, название компонента нужно выводить для всех представлений, поэтому его лучше вывести уже в методе контроллера. В методах представлений будем выводить только те элементы, которые требуют какой-либо информации из модели.
В файле /components/com_myquestions/controller.php измените код методов класса QuestionController так:
function display() { global $app; $pathway =& $app->getPathway(); $pathway->addItem(JText::_('COM_MYQUESTIONS'), JRoute::_('index.php?option=com_myquestions')); … } function showForm() { global $app; $pathway =& $app->getPathway(); $pathway->addItem(JText::_('COM_MYQUESTIONS'), JRoute::_('index.php?option=com_myquestions')); $pathway->addItem(JText::_('COM_MYQUESTIONS_ADD_QUESTION')); … }
Изменим метод отображения категории так, чтобы в навигационной цепочке выводилось название категории. Добавьте в метод QuestionViewCategory::display() в файле /components/com_myquestions/views/category/view.html.php код:
global $app; $pathway =& $app->getPathway(); $pathway->addItem($name_cat);
Метод QuestionViewQuestion::display() в файле /components/com_myquestions/views/question/tmpl/default.php измените так:
function display($tpl=null) { if ($tpl !== 'form') { global $option, $app; … $pathway =& $app->getPathway(); $pathway->addItem($question->name_cat, $this->link_cat); $pathway->addItem(JString::substr(strip_tags($question->question), 0, 15).'...'); } parent::display($tpl); }
В случае запроса шаблона default_form в данном методе не требуется никаких изменений, так как необходимые элементы навигационной цепочки уже были добавлены в контроллере. По этой же причине не нужно ничего изменять в методе display() представления all.
Добавьте в файл /language/ru-RU/ru-RU.com_myquestions.ini строку:
COM_MYQUESTIONS="Моя система «вопрос – ответ»"
Навигационная цепочка, которая теперь отображается во фронтенде компонента, показана на рис. 7.13.
Рис. 7.13. Навигационная цепочка на странице с выводом одного вопроса
Ключевые термины
JPagination | - класс для вывода элементов формы для разбивки на страницы списков элементов. |
JPathway | - класс для управления навигационной цепочкой. |
Краткие итоги
По сравнению с разработкой компонента написать модуль значительно легче, так как он, как правило, не использует собственных таблиц и не обрабатывает данные, введенные пользователем.
Joomla позволяет разбивать длинные списки на страницы, задавая длину списка по умолчанию. Для вывода списков элементов с разбивкой на страницы как в бэкенде, и так и во фронтенде используется класс JPagination. Его методы генерируют HTML-код таких элементов, как счетчик страниц, ссылки на предыдущую/следующую страницу и т.д.
Для управления навигационной цепочкой в Joomla существует объект JPathway. Его методы позволяют добавлять элементы в навигационную цепочку, изменять названия отдельных элементов или весь массив целиком.
Вопросы
- Почему разработать модуль легче, чем компонент?
- Каким образом Joomla позволяет разбивать длинные списки на страницы?
- Какой объект используется для управления навигационной цепочкой?