GrabDuck

Оптимизируем K2. Дерево категорий. Сокращаем количество запросов к базе данных.

:

   Примечание автора: тоесть, из за формирования дерева категорий K2 количество запросов к базе данных на Вашем сайте с отключенным кэшем или для авторизированных посетителей (после входа на сайт) будет равно (количество категорий * 3). В этой же статье мы сведём количество запросов к базе данных на формирование всего дерева категорий к одному.

   Путём мониторинга запросов к базе данных было найдено «узкое место».

   Первым слабым звеном здесь является показ количества материалов напротив категорий (+1 запрос на каждую категорию).

Убираем счётчик материалов в навигации категорий K2.

   Заходим в панель администрирования – в верхнем меню расширения - менеджер модулей – ищем модуль mod_k2_tools, отвечающий за вывод навигации по категориям, щелкаем по его названию (в параметрах модуля в выпадающем списке «Выберите функциональность модуля» должно быть отмечено «Категории (меню)»), переходим в правой части экрана к «Категории (меню) Настройки», отмечаем флаг «Счётчик материалов» – «скрыть», в верхнем правом углу экрана нажимаем «Сохранить».

   Итак, количество запросов каталога «Алтан» падает на треть. Их осталось 600. Что так же не приемлемо.

   И опять проблема кроется в модуле mod_k2_tools.

Переписываем mod_k2_tools. Один запрос на формирование дерева категорий.

   Открываем в директории Вашего сайта файл: /modules/mod_k2_tools/helper.php (в конце статьи Вы можете найти модифицированный файл helper.php для версии k2 2.5.4 или можете скачать его по ссылке, и распаковать в папку: /modules/mod_k2_tools/, заменив оригинальный helper.php).

   Ищем по поиску function hasChildren($id) { (приблизительно 337 строка – начало, 370 строка – конец для версии k2 2.5.4), заменяем всю функцию hasChildren на:

function hasChildren($id) {
		global $k2_categories;
		$mainframe = &JFactory::getApplication();
		$user = &JFactory::getUser();
		$aid = (int) $user->get('aid');
		$id = (int) $id;
		$db = &JFactory::getDBO();
		if(!is_array($k2_categories)){
		$query = "SELECT * FROM #__k2_categories  WHERE published=1 AND trash=0 ";
		if(K2_JVERSION=='16'){
			$query .= " AND access IN(".implode(',', $user->authorisedLevels()).") ";
			if($mainframe->getLanguageFilter()) {
				$languageTag = JFactory::getLanguage()->getTag();
				$query .= " AND language IN (".$db->Quote($languageTag).", ".$db->Quote('*').") ";
			}
		}
		else {
			$query .= " AND access <= {$aid}";
		}
		$db->setQuery($query);
		$rows = $db->loadResult();
		$k2_categories = $rows;
		if ($db->getErrorNum()) {
			echo $db->stderr();
			return false;
		}
		}
		$rows					 = array();
		foreach ($k2_categories as $category){
			if($category->parent == $id){
					$rows[]		 	=  $category;
					break;
			}
		}
		if (sizeof($rows)) {
			return true;
		} else {
			return false;
		}
	}

   Ищем по поиску function treerecurse (&$params, $id = 0, $level = 0, $begin = false) { : (приблизительно 395 строка - начало, 491 строка - конец для версии k2 2.5.4):

   Заменяем всю функцию treerecurse на:

        function treerecurse(&$params, $id = 0, $level = 0, $begin = false) {
		static $output;
		global $k2_categories;
		if ($begin) {
			$output = '';
		}
		$mainframe = &JFactory::getApplication();
		$root_id = (int) $params->get('root_id');
		$end_level = $params->get('end_level', NULL);
		$id = (int) $id;
		$catid = JRequest::getInt('id');
		$option = JRequest::getCmd('option');
		$view = JRequest::getCmd('view');
		$user = &JFactory::getUser();
		$aid = (int) $user->get('aid');
		$db = &JFactory::getDBO();
		switch ($params->get('categoriesListOrdering')) {
			case 'alpha':
				$orderby = 'name';
				break;
			case 'ralpha':
				$orderby = 'name DESC';
				break;
			case 'order':
				$orderby = 'ordering';
				break;
			case 'reversedefault':
				$orderby = 'id DESC';
				break;
			default:
				$orderby = 'id ASC';
				break;
		}
		$rows 			 = array();
		if(!is_array($k2_categories)){
		$query			 = "SELECT * FROM #__k2_categories WHERE published=1 AND trash=0 ";
		if(K2_JVERSION=='16'){
			$query .= " AND access IN(".implode(',', $user->authorisedLevels()).") ";
			if($mainframe->getLanguageFilter()) {
				$languageTag = JFactory::getLanguage()->getTag();
				$query .= " AND language IN (".$db->Quote($languageTag).", ".$db->Quote('*').") ";
			}
		}
		else {
			$query .= " AND access <= {$aid}";
		}
		$query .= " ORDER BY {$orderby}";
		$db->setQuery($query);
		$k2_categories = $db->loadObjectList();
		}
		if (($root_id != 0) && ($level == 0)) {
			foreach ($k2_categories as $category){
				if($category->parent == $root_id){
					$rows[]			 = $category;
				}
			}
		} else {
			foreach ($k2_categories as $category){
				if($category->parent == $id){
					$rows[]			 = $category;
				}
			}
		}
		if ($db->getErrorNum()) {
			echo $db->stderr();
			return false;
		}
		if ($level < intval($end_level) || is_null($end_level)) {
			$output .= '<ul class="level'.$level.'">';
			foreach ($rows as $row) {
				if ($params->get('categoriesListItemsCounter')) {
					$row->numOfItems = ' ('.modK2ToolsHelper::countCategoryItems($row->id).')';
				} else {
					$row->numOfItems = '';
				}
				if (($option == 'com_k2') && ($view == 'itemlist') && ($catid == $row->id)) {
					$active = ' class="activeCategory"';
				} else {
					$active = '';
				}
				if (modK2ToolsHelper::hasChildren($row->id)) {
					$output .= '<li'.$active.'><a href="'.urldecode(JRoute::_(K2HelperRoute::getCategoryRoute($row->id.':'.urlencode($row->alias)))).'"><span class="catTitle">'.$row->name.'</span><span class="catCounter">'.$row->numOfItems.'</span></a>';
					modK2ToolsHelper::treerecurse($params, $row->id, $level + 1);
					$output .= '</li>';
				} else {
					$output .= '<li'.$active.'><a href="'.urldecode(JRoute::_(K2HelperRoute::getCategoryRoute($row->id.':'.urlencode($row->alias)))).'"><span class="catTitle">'.$row->name.'</span><span class="catCounter">'.$row->numOfItems.'</span></a></li>';
				}
			}
			$output .= '</ul>';
		}
		return $output;
	}

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

   Итак, что же мы изменили?(Технические аспекты)

   Оригинальная функция hasChildren: (задачей которой является проверка, есть ли у данной категории дочерние категории? Если есть –    она запускает функцию treerecurse для формирования дерева дочерних категорий):

$query = "SELECT * FROM #__k2_categories  WHERE parent={$id} AND published=1 AND trash=0 ";

Данный запрос выбирает все категории, для которых указанная категория ($id) является родительской.

        if (count($rows)) {
			return true;
		} else {
			return false;
		}

   Если количество категорий больше «0», тогда он возвращает true, иначе false.

   Видоизменяем код:

   Объявляем глобальную переменную:

global $k2_categories;

Сразу после объявления функций.

   Если эта переменная ещё не является массивом (а такое может быть только один раз, после её объявления): выбираем все опубликованные категории, которые «не находятся в корзине» одним запросом к базе данных, и присваиваем полученный массив глобальной переменной $k2_categories (итого, к выборке массива категорий из базы данных мы обратимся всего лишь один раз – 1 запрос к базе данных, в дальнейшем же мы будем работать с мкассивом категорий напрямую):

if(!is_array($k2_categories)){
		$query = "SELECT * FROM #__k2_categories  WHERE published=1 AND trash=0 ";
		if(K2_JVERSION=='16'){
			$query .= " AND access IN(".implode(',', $user->authorisedLevels()).") ";
			if($mainframe->getLanguageFilter()) {
				$languageTag = JFactory::getLanguage()->getTag();
				$query .= " AND language IN (".$db->Quote($languageTag).", ".$db->Quote('*').") ";
			}
		}
		else {
			$query .= " AND access <= {$aid}";
		}
		$db->setQuery($query);
		$rows = $db->loadResult();
		$k2_categories = $rows;
		if ($db->getErrorNum()) {
			echo $db->stderr();
			return false;
		}
		}

   Далее в цикле обходим наш массив и выбираем дочерние категории для родительской:

$rows					 = array();
		foreach ($k2_categories as $category){
			if($category->parent == $id){
					$rows[]		 	=  $category;
					break;
			}
		}
if (sizeof($rows)) {
			return true;
		} else {
			return false;
		}

   Если есть хотя бы одна дочерняя категория: разрешаем функции treerecurse формирование списка дочерних категорий

function treerecurse(&$params, $id = 0, $level = 0, $begin = false) {

   Данная функция «формирует» дерево категорий, и отображает дочерние категории для родительской текущего уровня, для этого на для каждой подкатегории она проверяет наличие дочерних категорий, вызывая функцию hasChild (которую мы уже переписали), и выбирает список категорий текущего уровня.

if (($root_id != 0) && ($level == 0)) {
			$query = "SELECT * FROM #__k2_categories WHERE parent={$root_id} AND published=1 AND trash=0 ";
		} else {
			$query = "SELECT * FROM #__k2_categories WHERE parent={$id} AND published=1 AND trash=0 ";
		}
		if(K2_JVERSION=='16'){
			$query .= " AND access IN(".implode(',', $user->authorisedLevels()).") ";
			if($mainframe->getLanguageFilter()) {
				$languageTag = JFactory::getLanguage()->getTag();
				$query .= " AND language IN (".$db->Quote($languageTag).", ".$db->Quote('*').") ";
			}
		}
		else {
			$query .= " AND access <= {$aid}";
		}
		$query .= " ORDER BY {$orderby}";
		$db->setQuery($query);
		$rows = $db->loadObjectList();
		if ($db->getErrorNum()) {
			echo $db->stderr();
			return false;
		}

   Здесь функция добавляет по одному запросу к базе данных на текущую категорию + 1 запрос на проверку дочерних категорий (который мы уже исключили).

   Изменённый код:

   Добавляем нашу глобальную переменную, после определения функции:

		global $k2_categories;

Запрос к базе данных:

if (($root_id != 0) && ($level == 0)) {
			$query = "SELECT * FROM #__k2_categories WHERE parent={$root_id} AND published=1 AND trash=0 ";
		} else {
			$query = "SELECT * FROM #__k2_categories WHERE parent={$id} AND published=1 AND trash=0 ";
		}
		if(K2_JVERSION=='16'){
			$query .= " AND access IN(".implode(',', $user->authorisedLevels()).") ";
			if($mainframe->getLanguageFilter()) {
				$languageTag = JFactory::getLanguage()->getTag();
				$query .= " AND language IN (".$db->Quote($languageTag).", ".$db->Quote('*').") ";
			}
		}
		else {
			$query .= " AND access <= {$aid}";
		}
		$query .= " ORDER BY {$orderby}";
		$db->setQuery($query);
		$rows = $db->loadObjectList();
		if ($db->getErrorNum()) {
			echo $db->stderr();
			return false;
		}

   Заменяем на код:

if(!is_array($k2_categories)){
		$query			 = "SELECT * FROM #__k2_categories WHERE published=1 AND trash=0 ";
		if(K2_JVERSION=='16'){
			$query .= " AND access IN(".implode(',', $user->authorisedLevels()).") ";
			if($mainframe->getLanguageFilter()) {
				$languageTag = JFactory::getLanguage()->getTag();
				$query .= " AND language IN (".$db->Quote($languageTag).", ".$db->Quote('*').") ";
			}
		}
		else {
			$query .= " AND access <= {$aid}";
		}
		$query .= " ORDER BY {$orderby}";
		$db->setQuery($query);
		$k2_categories = $db->loadObjectList();
		}

   В котором мы проверяем, является ли глобальная переменная $k2_categories массивом, если нет, тогда выбираем все категории, которые опубликованы и не находятся в корзине, и присваиваем полученный массив нашей глобальной переменной:

После чего в цикле мы «выбираем» категории нужного подуровня (начиная с указанной в настройках модуля категории):

if (($root_id != 0) && ($level == 0)) {
			foreach ($k2_categories as $category){
				if($category->parent == $root_id){
					$rows[]			 = $category;
				}
			}
		} else {
			foreach ($k2_categories as $category){
				if($category->parent == $id){
					$rows[]			 = $category;
				}
			}
		}

   Далее функция treerecurse остаётся неизменной. Как итог – мы добились сокращения запросов к базе данных при формировании списка категорий с трёх на одну категорию до одного на формирование полного списка навигации.

   Скачать изменённый модуль Вы можете в конце статьи. После этого его необходимо распаковать в папку Вашего сайта /modules/mod_k2_tools/ и заменить оригинальный helper.php на тот, который идёт в архиве

   Пример сайтов, где работает данная модификация: каталог радиодеталей «Алтан» (k2 2.5.4), наш сайт - awesome-design.com

Недавно нашел хороший сайт для обмена электронных денег http://www.bestchange.ru/liqpay-rur-to-sberbank.html

Больше информации о веб технологиях можно узнать из нашего перечня всех статей на сайте:

Скачать файл:

    Notice: Undefined variable: gallery_on in /home/www/awesome/data/www/awesome-design.com/templates/beez/html/com_k2/templates/default/item.php on line 73
  • Изменённый helper.php K2 2.5 (419 загрузок)