Во фреймворке Joomla компоненты исполняются при помощи пунктов меню. Кроме того, используя пункт меню, компонент может устанавливать собственный загловок страницы в браузере, заголовок страницы компонента, а также использовать различные параметры. Если вы зайдете в менеджер меню и попытаетесь добавить пункт меню для нашего компонента, то не найдете в списке предложенных вариантов компонента HelloWorld!.
Добавление пункта меню
Добавить недостающий функционал довольно легко. Просто создайте файл site/views/helloworld/tmpl/default.xml с кодом:
<?xml version="1.0" encoding="utf-8"?>
<metadata>
<layout title="COM_HELLOWORLD_HELLOWORLD_VIEW_DEFAULT_TITLE">
<message>COM_HELLOWORLD_HELLOWORLD_VIEW_DEFAULT_DESC</message>
</layout>
</metadata>
Теперь в списке будет появляться наш HelloWorld!:
Правда на данный момент в административной части строки так и останутся не переведенными. В одной из следующих частей мы рассмотрим, как добавить поддержку мультиязычности и строки будут выглядеть более привлекательно.
Добавление параметров в меню
Сейчас наш компонент отображает только одно сообщение - Hello World!. Joomla 2.5 предоставляет возможность добавлять параметры в типы меню, тем самым мы можем расширить количество сообщений. В нашем случае, это делается в том же самом файле site/views/helloworld/tmpl/default.xml:
<?xml version="1.0" encoding="utf-8"?>
<metadata>
<layout title="COM_HELLOWORLD_HELLOWORLD_VIEW_DEFAULT_TITLE">
<message>COM_HELLOWORLD_HELLOWORLD_VIEW_DEFAULT_DESC</message>
</layout>
<fields name="request">
<fieldset name="request">
<field
name="id"
type="list"
label="COM_HELLOWORLD_HELLOWORLD_FIELD_GREETING_LABEL"
description="COM_HELLOWORLD_HELLOWORLD_FIELD_GREETING_DESC"
default="1"
>
<option value="1">Hello World!</option>
<option value="2">Good bye World!</option>
</field>
</fieldset>
</fields>
</metadata>
Здесь важно отметить, что request группа полей обозначает обязательные параметры. Мы добавили поле с типом list и список сообщений для этого поля.
Теперь необходимо изменить модель, чтобы она могла переключаться между разными сообщениями (которые выбираются пользователем в определенном выше поле):
site/models/helloworld.php
<?php
// Запрет прямого доступа.
defined('_JEXEC') or die;
// Подключаем библиотеку modelitem Joomla.
jimport('joomla.application.component.modelitem');
/**
* Модель сообщения компонента HelloWorld.
*/
class HelloWorldModelHelloWorld extends JModelItem
{
/**
* Получаем сообщение.
*
* @param int $id Id сообщения.
*
* @return string Сообщение, которое отображается пользователю.
*/
public function getItem($id = null)
{
// Если id не установлено, то получаем его из состояния.
$id = (!empty($id)) ? $id : (int) $this->getState('message.id');
if (!isset($this->_item))
{
switch ($id)
{
case 2:
$this->_item = 'Good bye World!';
break;
case 1:
default:
$this->_item = 'Hello World!';
break;
}
}
return $this->_item;
}
/**
* Метод для авто-заполнения состояния модели.
*
* Заметка. Вызов метода getState в этом методе приведет к рекурсии.
*
* @return void
*/
protected function populateState()
{
$app = JFactory::getApplication();
// Получаем Id сообщения из Запроса.
$id = $app->input->getInt('id', 0);
// Добавляем Id сообщения в состояние модели.
$this->setState('message.id', $id);
parent::populateState();
}
}
Обратите внимание, что мы используем метод populateState()
для автозаполнения состояния модели. Состояние модели - это объект JObject, который хранится в свойстве $state класса JModel. Метод populateState()
вызывается единожды при первой попытке установить состояние модели через метод setState(), которая происходит в методе getModel() контроллера.
Пока мы заполняем состояние модели лишь одной переменной - Id сообщения. Таким образом, мы можем получать доступ к текущему Id сообщения, не запрашивая его постоянно из объекта Запроса.
Поддержка настроек пункта меню
Для того, чтобы компонент мог изменять заголовок страницы в браузере, заголовок страницы компонента и метаданные в зависимости от настроек в пункте меню, нам необходимо добавить эту поддержку в представление site/views/helloworld/view.html.php:
<?php
// Запрет прямого доступа.
defined('_JEXEC') or die;
// Подключаем библиотеку представления Joomla.
jimport('joomla.application.component.view');
/**
* HTML представление сообщения компонента HelloWorld.
*/
class HelloWorldViewHelloWorld extends JViewLegacy
{
/**
* Сообщение.
*
* @var string
*/
protected $item;
/**
* Параметры.
*
* @var object
*/
protected $params;
/**
* Переопределяем метод display класса JViewLegacy.
*
* @param string $tpl Имя файла шаблона.
*
* @return void
*/
public function display($tpl = null)
{
try
{
// Получаем сообщение из модели.
$this->item = $this->get('Item');
// Получаем параметры приложения.
$app = JFactory::getApplication();
$this->params = $app->getParams();
// Подготавливаем документ.
$this->_prepareDocument();
// Отображаем представление.
parent::display($tpl);
}
catch (Exception $e)
{
JFactory::getApplication()->enqueueMessage(JText::_('COM_HELLOWORLD_ERROR_OCCURRED'), 'error');
JLog::add($e->getMessage(), JLog::ERROR, 'com_helloworld');
}
}
/**
* Подготавливает документ.
*
* @return void
*/
protected function _prepareDocument()
{
$app = JFactory::getApplication();
$menus = $app->getMenu();
$title = null;
// Так как приложение устанавливает заголовок страницы по умолчанию,
// мы получаем его из пункта меню.
$menu = $menus->getActive();
if ($menu)
{
$this->params->def('page_heading', $this->params->get('page_title', $menu->title));
}
else
{
$this->params->def('page_heading', JText::_('COM_HELLOWORLD_DEFAULT_PAGE_TITLE'));
}
// Получаем заголовок страницы в браузере из параметров.
$title = $this->params->get('page_title', '');
if (empty($title))
{
$title = $app->getCfg('sitename');
}
elseif ($app->getCfg('sitename_pagetitles', 0) == 1)
{
$title = JText::sprintf('JPAGETITLE', $app->getCfg('sitename'), $title);
}
elseif ($app->getCfg('sitename_pagetitles', 0) == 2)
{
$title = JText::sprintf('JPAGETITLE', $title, $app->getCfg('sitename'));
}
if (empty($title))
{
$title = $this->item;
}
// Устанавливаем заголовок страницы в браузере.
$this->document->setTitle($title);
// Добавляем поддержку метаданных из пункта меню.
if ($this->params->get('menu-meta_description'))
{
$this->document->setDescription($this->params->get('menu-meta_description'));
}
if ($this->params->get('menu-meta_keywords'))
{
$this->document->setMetadata('keywords', $this->params->get('menu-meta_keywords'));
}
if ($this->params->get('robots'))
{
$this->document->setMetadata('robots', $this->params->get('robots'));
}
}
}
Теперь представление, используя метод getParams()
получает параметры Приложения (которые включают в себя параметры меню), и пытается установить заголовок страницы компонента, заголовок страницы в браузере и метаданные исходя из имеющихся настроек в активном пункте меню.
Но откуда у Приложения появился метод getParams()
? В классе JApplication такого метода нет. Хитрость в том, что метод JApplication::getInstance() возвращает объект текущего приложения, класс которого располагается в файле /includes/application.php. Для публичной части - это JSite
, а для административной части - JAdministrator
. Метод getParams()
находится в классе JSite
.
И откуда у представления взялось свойство document
, c помощью которого мы манипулируем страницей? Если мы посмотрим на класс JView, то не обнаружим там такого свойства. Так как же оно попало в представление? А попало это свойство в представление из метода display() контроллера, который назначает его представлению, передавая объект класса JDocument. А точнее объект одного из дочерних классов: JDocumentHtml, JDocumentRaw и т.п. В нашем случае - это JDocumentHtml.
Изменям шаблон сообщения
Добавим вывод заголовка страницы компонента в шаблон site/views/helloworld/tmpl/default.php:
<?php
// Запрет прямого доступа.
defined('_JEXEC') or die;
?>
<?php if ($this->params->get('show_page_heading')) : ?>
<h1>
<?php echo $this->escape($this->params->get('page_heading')); ?>
</h1>
<?php endif; ?>
<h2><?php echo $this->item; ?></h2>
Здесь мы воспользовались методом escape(), чтобы экранировать спецсимволы HTML и избежать возможной XSS атаки.
Обработка ошибок
В Joomla по умолчанию используется свой внутренний механизм обработки ошибок с помощью классов JError и JException. Но начиная с версии Платформы 12.1 они отмечены как устаревшие и указано, что стоит использовать встроенный в PHP механизм исключений.
Наш компонент как можно меньше должен строится на устаревших классах или методах, поэтому в методе display()
задействован класс Exception
. Если возникло исключение, то мы делаем две вещи:
- Отображаем сообщение об ошибке пользователю с помошью метода enqueueMessage().
- Логируем ошибку с помощью метода JLog::add(). В первом параметре мы передаем ошибку, которую получаем из объекта исключения. Во втором параметре передаем уровень ошибки JLog::ERROR. А в третьем параметре указываем, что категория ошибки com_helloworld.
Но есть одно но. Чтобы использовать Exception
, необходимо установить свойство $legacy класса JError в значение false
. Самым лучшим местом для этого конечно же будет точка входа компонента site/helloworld.php. Добавьте сразу после подключения логирования следущий код:
// Устанавливаем обработку ошибок в режим использования Exception.
JError::$legacy = false;
Собираем пакет установки компонента
Не забудьте поменять номер версии в файле helloworld.xml:
<version>0.0.3</version>
helloworld.xml
<?xml version="1.0" encoding="utf-8"?>
<extension type="component" version="2.5.0" method="upgrade">
<name>Hello World!</name>
<!-- Следующие элементы необязательны -->
<creationDate>Июль 2012</creationDate>
<author>Вася Пупкин</author>
<authorEmail>Ваш e-mail</authorEmail>
<authorUrl>Ваш сайт</authorUrl>
<copyright>Информация о копирайте</copyright>
<license>Информация о лицензии</license>
<!-- Версия записывается в таблицу компонентов -->
<version>0.0.3</version>
<!-- Описание необязательно -->
<description>Описание компонента Hello World! ...</description>
<!-- Запускается при обновлении -->
<update>
<schemas>
<schemapath type="mysql">sql/updates/mysql</schemapath>
</schemas>
</update>
<!-- Раздел основных файлов сайта -->
<!-- Обратите внимание на значение аттрибута folder: Этот аттрибут описывает папку нашего пакета-установщика из которой должны копироваться файлы.
Поэтому указанные в этом разделе файлы будут скопированы из папки /site/ нашего пакета-установщика в соответствующую папку установки. -->
<files folder="site">
<filename>index.html</filename>
<filename>controller.php</filename>
<filename>helloworld.php</filename>
<folder>models</folder>
<folder>views</folder>
</files>
<!-- Администрирование -->
<administration>
<!-- Раздел Меню -->
<menu>Hello World!</menu>
<!-- Раздел основных файлов администрирования -->
<!-- Обратите внимание на значение аттрибута folder: Этот аттрибут описывает папку нашего пакета-установщика из которой должны копироваться файлы.
Поэтому указанные в этом разделе файлы будут скопированы из папки /admin/ нашего пакета-установщика в соответствующую папку установки. -->
<files folder="admin">
<filename>index.html</filename>
<filename>helloworld.php</filename>
<folder>sql</folder>
</files>
</administration>
</extension>
Содержимое директории с кодом:
helloworld.xml
site/index.html
site/controller.php
site/helloworld.php
site/models/index.html
site/models/helloworld.php
site/views/index.html
site/views/helloworld/index.html
site/views/helloworld/view.html.php
site/views/helloworld/tmpl/index.html
site/views/helloworld/tmpl/default.php
site/views/helloworld/tmpl/default.xml
admin/index.html
admin/helloworld.php
admin/sql/index.html
admin/sql/updates/index.html
admin/sql/updates/mysql/index.html
admin/sql/updates/mysql/0.0.1.sql
Запакуйте директорию в архивный файл (zip, tar, tar.gz, bz2) или скачайте напрямую с GitHub. Далее установите его, используя менеджер расширений Joomla. Теперь вы можете добавить и настроить пункт меню для компонента, и выбрать сообщение, которое будет отображаться пользователю.
В следущей части мы добавим поддержку базы данных.