В CMS Joomla есть возможность создавать категории и размещать в них материалы. Плюс ко всему, категории можно делать вложенными друг в друга, в результате чего получается дерево категорий. Это очень удобно и практично.
Если Вы пробовали использовать мой Модуль Effect Content для Joomla 3, то наверняка обращали внимание на поля выбора категорий для материалов Joomla, записей K2, товаров JoomShopping и объявлений AdsManager. Заметили, как удобно структурирован список? Все вложенные категории идут строго под своим родителем, что в свою очередь упрощает поиск необходимой.
Я давно уже хотел описать, как можно этого добиться.
Вообще, в самой Joomla есть встроенные механизмы генерации дерева категорий. Но они работают не совсем так, как мне было нужно. Поиски в интернете и на различных форумах результата особо не принесли.
В этой статье я постараюсь описать наиболее распространенные варианты деревьев категорий, которые Вам могут понадобиться. А также опишу те, которые использую я.
Как это работает?
Код будет адаптирован исключительно под Joomla. Имея немного опыта в PHP, Вы самостоятельно сможете переделать его под себя.
Вариант 1. Обычный список.
Самый простой вариант, это если Вам понадобится просто составить список всех категорий.
<?php
$db = JFactory::getDBO();
$query = $db->getQuery(true);
$query->select('`id`, `title`, `parent_id`');
$query->from($db->quoteName('#__categories'));
$query->where($db->quoteName('extension') . ' LIKE "com_content"');
$query->order($db->quoteName('lft') . ' ASC');
$db->setQuery($query);
$results = $db->loadObjectList();
$categories = array();
if (count($results)) {
$categories[] = JHTML::_('select.option', '0', 'Выберите категорию', 'id', 'title'); // Добавляем пустую первую строку, не обязательно.
$categories = array_merge($categories, $results); // Добавляем результаты выборки в общий массив
echo JHtml::_('select.genericlist', $categories, 'list-categories', 'class="select-list"', 'id', 'title'); // Формируем список select с помощью JHtml
}
?>
Как видим, здесь всё просто. С помощью запроса делаем выборку категорий, если вернулся результат, выводим сформированный список.
Обратите внимание на строки ‘id’ и ‘title’ в JHTML, они должны быть равны значениям выборки. Т.е. из базы данных мы получаем id и title, соответственно в JHTML мы сообщаем что value = id, а text = title.
К сожалению такой вариант мне совершенно не подошел, да и статья у нас именно про дерево категорий, а не просто список.
Вариант 2. Структурированное дерево категорий.
<?php
$db = JFactory::getDBO();
$query = $db->getQuery(true);
$query->select('`id`, `title`, `parent_id`');
$query->from($db->quoteName('#__categories'));
$query->where($db->quoteName('extension') . ' LIKE "com_content"');
$query->order($db->quoteName('lft') . ' ASC');
$db->setQuery($query);
$results = $db->loadObjectList();
$categories = array();
if (count($results)) {
$temp_options = array();
$categories[] = JHTML::_('select.option', '0', 'Выберите категорию', 'id', 'title'); // Добавляем пустую первую строку, не обязательно.
foreach($results as $item) { // Формируем нужный вид массива
array_push($temp_options, array($item->id, $item->title, $item->parent_id));
}
foreach($temp_options as $option) {
if ($option[2] == 1) { // Обратите внимание на условие сравнения, подробнее об этом ниже
$categories[] = JHTML::_('select.option', $option[0], $option[1], 'id', 'title');
recursive_options($temp_options, 1, $option[0], $categories);
}
}
echo JHTML::_('select.genericlist', $categories, 'list-categories', 'class="select-list"', 'id', 'title');
}
function recursive_options($temp_options, $level, $parent, &$categories) {
foreach($temp_options as $option) {
if ($option[2] == $parent) {
$level_string = '';
for ($i = 0; $i < $level; $i++) {
$level_string .= '- ';
}
$categories[] = JHTML::_('select.option', $option[0], $level_string . $option[1], 'id', 'title');
recursive_options($temp_options, $level+1, $option[0], $categories);
}
}
}
?>
Как видите, здесь уже немного сложнее, но принцип тот же.
Сперва мы делаем точно такой же запрос, как и в первом варианте. Далее, если у нас есть результаты выборки, мы опять добавляем пустую строку.
После этого переформировываем массив нужным образом, это пригодится для правильного создания списка.
Как только массив готов, мы проходим по нему циклом. В цикле используется вызов функции проверки на родителя и генерации элемента списка.
Обратите внимание на первое условие до вызова функции:
if ($option[2] == 1) {
Здесь мы делаем проверку, что у нас именно первый уровень. $option[2] — это у нас родитель категории, он должен быть равен единице. Т.к. в категориях Joomla самым корнем является ROOT с id = 0, но при этом он имеет тип system, а в выборке мы получаем только `extension` LIKE ‘com_content’, то нам остается делать проверку именно на единицу, будьте внимательнее. Если Вы делаете дерево категорий JoomShopping, то проверку уже делаем на ноль:
if ($option[2] == 0) {
Дело в том, что там нет корневой категории и родителем является ноль (0). Вы должны понимать это, иначе либо получите пустой список, либо бесконечное зацикливание.
Если не сделать эту проверку, то дерево построится столько раз, сколько будет категорий в выборке, а может и вообще зависнуть.
В самой же функции есть примерно такое же условие:
if ($option[2] == $parent) {
Здесь уже идет сравнение родителя с текущим, поэтому ничего не меняем, иначе у каждой родительской категории будет список из всех категорий.
Именно этот вариант я предпочел использовать в модуле Effect Content для Joomla 3.
Вариант 3. Дерево категорий по заданной категории.
А теперь предлагаю разобрать более сложный вариант.
В одном из заказов мне необходимо было сделать фильтр материалов Joomla, в котором категории играли роль городов. Но, помимо городов там были еще и обычные категории.
В этом случае ни первый, ни второй вариант мне не подошли, т.к. в обоих выводится весь список категорий, а нужно строить дерево только из заданных.
Ответ на этот вопрос я так и не смог найти на просторах интернет. Либо никто не задавался подобным, либо я плохой искатель.
Бросив поиски, я начал писать новую функцию. Да, она у меня получилась, но, к сожалению, она оказалась настолько тяжелой, что при большом количестве категорий и уровнях вложенности сайт может просто падать.
Основная ирония в том, что перед написанием функции, я совсем забыл про второй вариант, описанный выше, и лишь когда функция была готова и сайт пару раз упал, я вспомнил про универсальность функции, описанной во втором варианте.
В итоге я решил не описывать здесь новую функцию, чтобы уберечь Вас от моих же ошибок, к тому же люди еще засмеют меня.
Так вот, помните во втором примере есть самое первое условие?
if ($option[2] == 1) {
Здесь мы указываем, что будем формировать список от родителя id которого равен 1 (или 0, если корень категорий равен 0).
Просто укажите id категории, от которой необходимо строить дерево, и всё. К примеру, нам необходимо построить дерево категорий от категории с id = 11, условие будет таким:
if ($option[2] == 11) {
Если же Вам нужно включить в дерево всех потомков от нескольких категорий, то ничего сложного. Условие примет вид примерно такой:
if ($option[2] == 11 || $option[2] == 15) {
Вариант 4. Для тех кто не использует Joomla.
И напоследок функция из второго варианта для тех, кто не использует Joomla.
<?php
// Здесь Ваша выборка
if (count($results)) {
$temp_options = array();
foreach($results as $item) {
array_push($temp_options, array($item->id, $item->title, $item->parent_id));
}
echo '<select name="list-categories" class="select-list">';
echo '<option value="0">Выберите категорию</option>';
foreach($temp_options as $option) {
if ($option[2] == 1) {
echo '<option value="' . $option[0] . '">' . $option[1] . '</option>';
recursive_options($temp_options, 1, $option[0]);
}
}
echo '</select>';
}
function recursive_options($temp_options, $level, $parent) {
foreach($temp_options as $option) {
if ($option[2] == $parent) {
$level_string = '';
for ($i = 0; $i < $level; $i++) {
$level_string .= '- ';
}
echo '<option value="' . $option[0] . '">' . $level_string . $option[1] . '</option>';
recursive_options($temp_options, $level+1, $option[0]);
}
}
}
?>
Здесь предполагается, что Вы самостоятельно сделаете выборку категорий и просто передадите полученный массив в функцию.
Всё доступно и просто.
Если Вы знаете другие, более простые варианты создания дерева категорий, пишите в комментариях.
Комментарии
Здесь еще никто не оставлял комментарии.