JRoute - Создание SEF (ЧПУ) ссылок / Маршрутизация в компоненте
Обзор маршрутизации
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.