Обзор маршрутизации

Joomla может создавать и синтаксически разбирать URL-адреса в разных форматах, в том числе и в человекопонятном. Одно из преимуществ заключается в том, что преобразование ссылок работает, даже если на сервере нет apache-модуля mod_rewrite.

Хорошим примером этого является статья «Добро пожаловать в Joomla». Первая ссылка создана без mod_rewrite, а вторая с mod_rewrite:

  • http://www.example.com/index.php/the-news/1-latest-news/1-welcome-to-joomla
  • http://www.example.com/the-news/1-latest-news/1-welcome-to-joomla

Подготовка данных для маршрутизации

Псевдоним

Первым шагом является формирование так называемого псевдонима. Псевдоним используется в URL вместо заголовка «(заголовок это текст, который вы хотите видеть в URL). Псевдоним должен быть URI-безопасным, что означает замену UTF-8 символов их эквивалентами ASCII7, пробельных — дефисами и т. д.

Псевдоним может быть определен самим пользователем, но вы должны обеспечить, чтобы были соблюдены вышеуказанные требования к безопасному URL. Хороший способ сделать это заключается в использовании метода JTable:check() в процессе сохранения. Вы можете посмотреть на этот пример кода:

function check()
{
    jimport('joomla.filter.output');
    if(empty($this->alias))
    {
	$this->alias = $this->title;
    }
    $this->alias = JFilterOutput::stringURLSafe($this->alias);
    /* Ваши остальные проверки */
    return true;
}

Если поле псевдонима будет пусто, то заголовок будет использоваться как псевдоним. Тогда псевдоним будет сделан методом JFilterOutput:stringURLSafe().

Строка

Продолжая тот же самый пример, «строка» — «1­-welcome­-to­-joomla» состоит из двух частей. Первая часть — идентификатор статьи, и вторая — псевдоним. Они отделены дефисом. Эти два элемента были объединены во время запроса к базе данных в модели:

$query = 'SELECT a.*,'.
        ' CASE WHEN CHAR_LENGTH(a.alias) THEN CONCAT_WS(\':\', a.id, a.alias) ELSE a.id END as slug,'.
        [...];

После этого шага строка используется вместо идентификатора.

Маршрутизация URL’s

Метод JRoute: _() преобразует внутренние ссылки Joomla в SEF-ссылки. JRoute имеет три параметра, вот её прототип:

JRoute: _($url, $xhtml = true, $ssl = 0);

Где:

  • $url — абсолютный или относительной внутренний URL Joomla.
  • $xhtml — булево значение, которое указывает, должен ли вывод быть в XHTML. Этот параметр не обязательный, и равен true по умолчанию.
  • $ssl — целое число, которое определяет должен ли URI быть безопасным.

Наиболее важным параметром является $url.

Пример вызова этого метода:

JRoute::_('index.php?view=article&id='.$row->slug);

$row->slug это значение, полученное сочетанием id и псевдонима во втором шаге.

Еще одно преимущество использования JRoute состоит в том, что теперь маршрутизатор обрабатывает $option (имя компонента) и $Itemid (id пункта меню). Компонент сам по себе не должен знать своё имя ($option) или id активного пункта меню ($Itemid), как это было в предыдущей версии Joomla.

Очень важно, что вы думаете по поводу последовательности в URL параметра в этой стадии. Это будет более понятно, когда мы глубже взглянем на router.php в следующем разделе.

Процесс создания JRouter делится на два этапа:

  • Создание маршрута приложения. Маршрутизация полностью обрабатываются JRouter и разработчику компонента не придется ничего делать, чтобы она работала.
  • Создание маршрута компонента. Для создания маршрута компонента, JRouter ищет в каталоге компонента, файл router.php который отвечает за создание маршрута для компонента.

Маршрутизатор

У нас будут две функции в router.php. Одна отвечает за создание URL, и другая за его синтаксический разбор. В следующих примерах, очень основном и более продвинутом, мы предполагаем, что у нас есть три представления, на которые могут вести ссылки. Первым представлением является краткий обзор категорий (view=categories), вторым является единственная категория (view=category), и третьей является единственная статья (view=article).

Файл router.php должен быть в области сайта вашего компонента. Это не используется на страницах администрации / внутренних страницах. Не забывайте добавлять router.php к своей инсталляции XML в. папке сайта.

Простой пример

Этот простой пример показывает основы работы маршрутизатора вашего компонента.

function [Componentname]BuildRoute(&$query)
{
    $segments = array();
    if(isset($query['view']))
    {
        $segments[] = $query['view'];
        unset($query['view']);
    }
    if(isset($query['id']))
    {
        $segments[] = $query['id'];
        unset($query['id']);
    };
    return $segments;
}

JRouter передает массив $query в функцию [componentname]BuildRoute. Эта функция добавит соответствующие части массива массиву $segments в правильном порядке и возвратит должным образом полученный массив.

Элементы массива $query должны быть удалены, иначе JRouter добавит их в URL в. виде строки запроса (т. е. любые переменные, которые не обрабатываются маршрутизатором будут использованы как параметры в строке запроса).

Следующая функция в router.php разбирает URL:

function [Componentname]ParseRoute($segments)
{
    $vars = array();
    switch($segments[0])
    {
        case 'categories':
            $vars['view'] = 'categories';
            break;
        case 'category':
            $vars['view'] = 'category';
            $id = explode(':', $segments[1]);
            $vars['id'] = (int)$id[0];
            break;
        case 'article':
            $vars['view'] = 'article';
            $id = explode(':', $segments[1]);
            $vars['id'] = (int)$id[0];
            break;
    }
    return $vars;
}

Что происходит здесь? В функции [componentname]BuildRoute мы упорядочили элементы в массив в определенной последовательности. Это означает, что в этом примере представление является первым, catid является вторым, и идентификатор третьим в массиве. Читая $segments[0], мы получаем название представления. Мы устанавливаем правильное представление и/или идентификатор в зависимости от его значения, и мы возвращаем массив $vars в JRouter. $vars должен быть ассоциативным массивом, подобным массиву, который передали к методу BuildRoute.

Вышеупомянутый пример router.php очень простой способ генерировать sef URL, он должен показать принцип работы маршрутизатора.

Сгенерированный URL в. этом примере содержит название представления и не отражает иерархию информационного наполнения:

http://www.example.com/[псевдоним_пункта_меню]/[представление]/[slug]

Более сложный пример

В следующем примере мы попытаемся избавиться от потребности в представлении, и попытаемся отразить в URL текущий уровень иерархии.

Цель состоит в том, что URL должен выглядеть следующим образом:

  • Когда смотрим статью: http://www.example.com/[псевдоним пункта меню]/[категория]/[статья]
  • Когда смотрим категорию: http://www.example.com/[псевдоним пункта меню]/[категория]
  • Когда смотрим список категорий: http://www.example.com/[псевдоним пункта меню]

Давайте представим, что мы сделали шаг 1 и 2, также для данной категории.

Ссылка на статьи будет выглядеть следующим образом:

JRoute::_('index.php?view=article&catid='.$row-­>catslug.'&id='.$row-­>slug);

И ссылка на категорию будет выглядеть так:

JRoute::_('index.php?view=category&id='.$row->catslug);

Соответствующий router.php:

function [Componentname]BuildRoute(&$query)
{
    $segments = array();
    if(isset($query['catid']))
    {
        $segments[] = $query['catid'];
        unset($query['catid']);
    }
    if(isset($query['id']))
    {
        $segments[] = $query['id'];
        unset($query['id']);
    };
    unset($query['view']);
    return $segments;
}

Разница заключается в том, что сейчас мы не добавляем имя представления к массиву $segments. Мы 

по-прежнему отключаем представление, поскольку в противном случаеJRouter добавил бы его в качестве части URL в. строке запроса. Еще одним нововведением является параметр catid, который мы добавляем в массив $segments.

function ['Componentname']ParseRoute($segments)
{
    $vars = array();
    $menu =& JMenu::getInstance();
    $item =& $menu->getActive();
    // Количество сегментов
    $count = count($segments);
    // Выбор представления и идентификатора
    switch( $item­>query['view'])
    {
        case 'categories':
            if($count == 1)
            {
                $vars['view'] = 'category';
            }
            if($count == 2)
            {
                $vars['view'] = 'article';
            }
            $id = explode(':', $segments[$count-1]);
            $vars['id'] = (int)$id[0];
            break;
        case 'category':
            $id   = explode(':', $segments[$count-1]);
            $vars['id']   = (int) $id[0];
            $vars['view'] = 'article';
            break;
        }
        return $vars;
    }
}

Вы видите, что у этой функции ParseRoute в коде есть много отличий по сравнению с предыдущей. Причина для этого проста. У нас нет названия представления в массиве $segments, и мы должны определить его другим способом.

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

$item->query['view']

Также мы должны знать число элементов в массиве $segments:

$count = count($segments);

Имея эту информацию мы можем правильно установить представление для всех возможных трех случаев:

  • Если пункт меню — ссылка на представление категорий, и у массива $segments есть два элемента ($catid и $id), мы знаем, что должны анализировать ссылку на статью.
  • Если пункт меню — ссылка на представление категорий и у массива $segments есть один элемент $id, мы знаем что должны анализировать ссылку на категорию.
  • Если пункт меню — ссылка на категорию, мы знаем, что любой элемент в массиве $segments — идентификатор для статьи.

Результат всего этого кода — хороший и человеческий читаемый URL.