Опубликован: 02.10.2012 | Доступ: свободный | Студентов: 1856 / 70 | Длительность: 11:55:00
Теги: joomla, mvc, php, xhtml, xml
Лекция 4:

Иерархия пунктов меню. Отправка писем. Классы ядра JEditor, JURI, JError, JDate

< Лекция 3 || Лекция 4: 123 || Лекция 5 >

Разработка фронтенда

Для удобства дальнейшего тестирования добавим еще несколько вопросов в различные категории:

INSERT INTO `jos_myquestions` (`id`, `name`, `date`, `question`, `city`, `email`, `IP`, `id_cat`)
VALUES (NULL, 'Аноним', '2012-01-02 09:01:00', 'Быть или не быть?', 'Москва',
 'somebody@mail.ru', '12.345.67.890', '1');
INSERT INTO `jos_myquestions` (`id`, `name`, `date`, `question`, `city`, `email`, `IP`, `id_cat`)
VALUES (NULL, 'Аноним', '2012-01-01 09:02:00', 'А судьи кто?', 'Москва', 
 'somebody@mail.ru', '12.345.67.890', '2');
INSERT INTO `jos_myquestions` (`id`, `name`, `date`, `question`, `city`, `email`, `IP`, `id_cat`) 
VALUES (NULL, 'Аноним', '2012-01-01 09:03:00', ' А был ли мальчик?', 'Москва',
 'somebody@mail.ru', '12.345.67.890', '2');
        

Для наглядности выключите SEF в настройках сайта: в бэкенде зайдите в меню "Сайт" - "Общие настройки" и установите переключатель "Включить SEF (ЧПУ)" в значение "Нет".

Вывод списка категорий

Замените содержимое файла /components/com_myquestions/myquestions.php (обратите внимание, что мы больше не работаем с папкой /administrator) на следующий код:

<?php
defined('_JEXEC') or die('Restricted access');
$option = JRequest::getVar('option'); $task = JRequest::getVar('task');
jimport('joomla.application.helper');
require_once(JApplicationHelper::getPath('html'));
JTable::addIncludePath(JPATH_ADMINISTRATOR.DS.'components'.
DS.$option.DS.'tables');
switch($task)
{
  default:
    showCategories($option);
    break;
}
function showCategories($option)
{
  $db =& JFactory::getDbo();
  $query = "SELECT c.id, c.name, c.desc ".
        "FROM #__myquestions_categories c";
  $db->setQuery($query);
  $rows = $db->loadObjectlist();
  if ($db->getErrorNum())
  {
    echo $db->stderr();
    return false;
  }
  HTML_questions::showCategories($rows, $option);
}
?>
            

Мы подключаем файл myquestions.html.php с помощью require_once(JApplicationHelper::getPath('html')) и папку table бэкенда с помощью JTable::addIncludePath(). Обратите внимание, что мы обращаемся к классам таблиц бэкенда, хотя пишем фронтенд компонента.

Переключатель switch обрабатывает пока только значение задачи по умолчанию, вызывая функцию для получения списка категорий.

Теперь добавим класс HTML_questions для фронтенда. Создайте файл /components/com_myquestions/myquestions.html.php:

<?php
class HTML_questions
{
  function showCategories($rows, $option)
  {
    ?>
    <p><a href='index.php?option=<?=$option?>&task=showlist'><?=
     JText::_('COM_MYQUESTIONS_ALL_QUESTIONS')?></a></p>
    <table>
    <?php
    foreach($rows as $row)
    {
      $link = 'index.php?option='.$option.'&id_cat='.$row->id.'&task=showlist';
      echo '<tr><td><p><a href="' . $link . '">'.$row->name
      .'</a></td><td>'.$row->desc.'</td></tr>';
    }
    ?>
    </table>
    <?php
  }
}
?>
            

Класс HTML_questions будет содержать все функции вывода во фронтенде. Функция showCategories() принимает в качестве параметров массив записей таблицы базы данных и название текущего компонента. В цикле осуществляется обход этого массива и каждая запись выводится на экран.

Создайте файл /language/ru-RU/ru-RU.com_myquestions.ini и скопируйте в него строку:

COM_MYQUESTIONS_ALL_QUESTIONS="Все вопросы"
            

Теперь на странице компонента myquestions во фронтенде по умолчанию отображается список категорий ( рис. 4.9).

Вывод списка категорий во фронтенде

увеличить изображение
Рис. 4.9. Вывод списка категорий во фронтенде
Просмотр списка вопросов

Измените код конструкции switch в файле myquestions.php, добавив обработку задачи showlist:

case 'showlist':
  showQuestions($option);
  break;
            

Добавьте в этот же файл функцию showQuestions():

function showQuestions($option)
{
  $db =& JFactory::getDbo();
  $query = "SELECT q.id,q.name,q.date,q.question,q.city,q.email,q.answer,q.id_cat,c.name AS cname ".
        "FROM #__myquestions q, #__myquestions_categories c ".
        "WHERE q.id_cat=c.id AND q.answer <> '' AND (q.published = 1 OR 
        (q.expiration_date <> '0000-00-00 00:00:00' AND q.expiration_date > NOW()))";

  $id_cat = JRequest::getVar('id_cat', '0');
  
  if ($id_cat != 0)
    $query .= "  AND q.id_cat = $id_cat";
  $db->setQuery($query);
  $rows = $db->loadObjectlist();
  if ($db->getErrorNum())
  {
    echo $db->stderr();
    return false;
  }
  
  if ($id_cat != 0)
  {
    $query = "SELECT name FROM #__myquestions_categories WHERE id=$id_cat";
    $db->setQuery($query);
    if ($db->getErrorNum())
    {
      echo $db->stderr();
      return false;
    }
    $name_cat = $db->loadResult();
  }
  else
    $name_cat = '';
  
  HTML_questions::showQuestions($rows, $option, $name_cat);
}
            

Длинный SQL-запрос требует пояснения. Как вы помните, при описании функциональности нашего компонента упоминалось, что отображаться на сайте будут вопросы, удовлетворяющие следующим условиям:

  • есть ответ;
  • либо вопрос не помечен как скрытый, либо дата снятия вопроса с публикации указана и больше текущей даты.

Данный запрос выбирает вопросы, которые соответствуют этим условиям.

Проверяется значение переменной $id_cat (id категории), и если категория задана, то из базы выбираются только вопросы, отнесенные к этой категории. Затем извлекается название категории для отображения над списком вопросов. Если же категория не задана, то выбираются вопросы из всех категорий.

Добавьте в класс HTML_questions в файле myquestions.html.php следующую функцию:

function showQuestions($rows, $option, $name_cat)
{
  if ($name_cat !== '')
    echo "<h1>$name_cat</h1>";

  foreach($rows as $row)
  {
    $link = 'index.php?option='.$option.'&id='.$row->id.'&task=showquestion';
    $link_cat = 'index.php?option='.$option.'&id_cat='.$row->id_cat.'&task=showlist';
    ?>
    <table width="100%">
      <tr>
        <td><i><?=$row->name?></i></td>
        <td><i><u><?=$row->email?></u></i></td>
        <td><i><?=JHTML::_('date', $row->date,
        JText::_('DATE_FORMAT_LC3'))?></i></td>
        <td><i><?=$row->city?></i></td>
      </tr>
      <tr>
        <td colspan="4"><a href="<?=$link_cat?>"><?=$row->cname?></a></td>
      </tr>
      <tr>
        <td colspan="4"><b><?=$row->question?></b></td>
      </tr>
      <tr>
        <td colspan="4"><?=$row->answer?></td>
      </tr>
      <tr>
        <td colspan="4"><a style="text-decoration: none;"
         title="<?=JText::_('COM_MYQUESTIONS_READMORE')?>" alt="<?=JText::_
         ('COM_MYQUESTIONS_READMORE')?>" href="<?=$link?>">---></a></td>
      </tr>
    </table>
    <br/>
    <?
  }
}
            

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

Добавьте в файл language/ru-RU/ru-RU.com_myquestions.ini строку:

COM_MYQUESTIONS_READMORE="Подробнее"
            

Для проверки работоспособности нового кода нажмите на ссылку "Все вопросы" во фронтенде нашего компонента. Результат должен быть приблизительно таким, как на рис. 4.10.

Список вопросов во фронтенде

увеличить изображение
Рис. 4.10. Список вопросов во фронтенде
Вывод одного вопроса

Иногда может понадобиться вывести какой-либо один вопрос. Например, если пользователь захочет поделиться ссылкой на свой вопрос с кем-то другим.

Измените код конструкции switch в файле myquestions.php, добавив обработку задачи showquestion:

case 'showquestion':
  showQuestion($option);
  break;
            

Добавьте в этот же файл функцию showQuestion():

function showQuestion($option)
{
  $id = JRequest::getVar('id', 0) ;
  $row = &JTable::getInstance ('question', 'Table');
  $row->load($id);
  
  if ($row->answer == '' || ($row->published == 0 && ($row->expiration_date ==
   '0000-00-00 00:00:00' || strtotime($row->expiration_date) <= time())))
    JError::raiseError(404, JText::_(' COM_MYQUESTIONS_ERROR404'));
  
  $row_cat =& JTable::getInstance('Category','Table');
  $row_cat->load($row->id_cat);
  
  HTML_questions::showQuestion($row, $option, $row_cat);
}
            

В данном коде мы получаем id из запроса к серверу с помощью функции getVar(). Если значение id отсутствует или неприемлемо, будет использовано значение по умолчанию, переданное во втором параметре (ноль). Затем мы получаем экземпляр класса таблицы из бэкенда и загружаем запись таблицы базы данных, соответствующую идентификатору id.

Далее происходит проверка того, можно ли отображать в открытом доступе данный вопрос. Исходя из условий отображения вопроса на сайте, нетрудно заметить, что не должны отображаться на сайте вопросы, удовлетворяющие хотя бы одному из условий:

  • нет ответа;
  • вопрос помечен как скрытый и дата снятия вопроса с публикации не указана или меньше или равна текущей дате.

Если вопрос не должен отображаться, то используется функция raiseError() класса JError для вывода сообщения "404 - Вопрос не найден". Такое же сообщение появится при попытке обращения к несуществующей записи.

Добавьте в файл language/ru-RU/ru-RU.com_myquestions.ini строку:

COM_MYQUESTIONS_ERROR404="Вопрос не найден"
            

Проверьте, что при обращении к вопросу, который не должен отображаться, выводится сообщение ( рис. 4.11).

Ошибка 404 "Вопрос не найден"

увеличить изображение
Рис. 4.11. Ошибка 404 "Вопрос не найден"

Теперь создадим функцию в классе вывода. Добавьте функцию showQuestion() в класс HTML_questions в файле myquestions.html.php:

function showQuestion($row, $option, $row_cat)
{
  $link_cat = 'index.php?option='.$option.'&id_cat='.$row->id_cat.'&task=showlist';
  ?>
  <p><a href='index.php?option=<?=$option?>&task=showlist'><?=JText::_
  ('COM_MYQUESTIONS_ALL_QUESTIONS')?></a></p>
  <table width="100%">
    <tr>
      <td><i><?=$row->name?></i></td>
      <td><i><u><?=$row->email?></u></i></td>
      <td><i><?=JHTML::_('date', $row->date,JText::_
      ('DATE_FORMAT_LC3'))?></i></td>
      <td><i><?=$row->city?></i></td>
    </tr>
    <tr>
      <td colspan="4"><a href="<?=
      $link_cat?>"><?=$row_cat->name?></a></td>
    </tr>
    <tr>
      <td colspan="4"><b><?=$row->question?></b></td>
    </tr>
    <tr>
      <td colspan="4"><?=$row->answer?></td>
    </tr>
  </table>
  <?
}
            

Данная функция аналогична функции showQuestions() за исключением того, что выводит не массив вопросов, а один вопрос.

Теперь в списке вопросов стрелка под каждым из них ("--->") ведет на страницу с выводом этого вопроса ( рис. 4.12).

Просмотр вопроса

увеличить изображение
Рис. 4.12. Просмотр вопроса

Ключевые термины

JDate - класс для работы с датами.
JEditor - класс для работы с WYSIWYG-редактором.
JError - класс для работы с ошибками.
JMail - класс для создания и отправки электронных писем.
JMailHelper - класс для очистки данных перед добавлением к электронному письму и проверки, является ли заданная строка корректным адресом электронной почты.
JURI - класс для работы с URI.
Иерархия пунктов меню - дерево, состоящее из пунктов меню и организованное с помощью вложенных множеств.

Краткие итоги

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

Создавать и отправлять электронные письма удобнее с помощью методов класса JMail.

Для работы с WYSIWYG-редактором используется класс JEditor.

Класс JURI позволяет работать с URI текущей или любой другой страницы.

Для работы с ошибками и предупреждениями может быть использован класс JError.

Для работы с датами в Joomla существует класс JDate.

Вопросы

  1. Каким образом в базе данных Joomla хранится иерархия пунктов меню?
  2. Какой класс позволяет создавать и отправлять электронные письма?
  3. Каким образом можно отобразить код выбранного администратором сайта WYSIWYG-редактора?
  4. Какой класс существует для работы с URI?
  5. Для чего может быть использован класс JError?
  6. Каким образом можно работать с датами в Joomla?

Упражнения

Адаптируйте код из раздела "Практика" для своего варианта (см. список вариантов в "Варианты заданий для лабораторных работ" ).

< Лекция 3 || Лекция 4: 123 || Лекция 5 >
Кирилл Гусаров
Кирилл Гусаров

В разделе "Первые папки и файлы. Добавление пунктов меню"

предлагается создать две файла:

- myquestions.php;

- admin.myquestions.php

с соответствуюшими адресами:

/components/com_myquestions/myquestions.php;

- /administrator/components/com_myquestions/admin.myquestions.php;

Так вот, при создании файла "admin.myquestions.php" В админке выдает ошибку - "Компонент не найден", а при переименовании его на  "myquestions.php" в последующем шаге, в админке не выводятся кнопки редактирования. 
Проверил кодировку, проверил правильность пути к файлам, пересохранил указанный код. Скажите что я делаю не так или в чем может быть причина?

Наталья Алмаева
Наталья Алмаева
Россия
Сергей Козлов
Сергей Козлов
Россия, Тюмень