Очень часто мелькают темы в которых упоминается про категории которые создают большую нагрузку, например в поиске или для drop-down меню. При этом всё что предлагают - отключать такие блоки при большом количестве категорий. Но на самом деле отключать не обязательно... Рекурсивный метод выборки данных самый распространённый, но не единственный. В случаях когда надо извлекать всё дерево категорий можно обойтись одним запросом не зависимо от количества уровней и количества элементов.
Себе я делал так:
В модель категорий добавил метод
public function getAllCategories() {
$category_data = $this->cache->get('category.all.' . $this->config->get('config_language_id') . '.' . (int)$this->config->get('config_store_id'));
if (!$category_data || !is_array($category_data)) {
$query = $this->db->query("SELECT * FROM " . DB_PREFIX . "category c LEFT JOIN " . DB_PREFIX . "category_description cd ON (c.category_id = cd.category_id) LEFT JOIN " . DB_PREFIX . "category_to_store c2s ON (c.category_id = c2s.category_id) WHERE cd.language_id = '" . (int)$this->config->get('config_language_id') . "' AND c2s.store_id = '" . (int)$this->config->get('config_store_id') . "' AND c.status = '1' AND c.sort_order <> '-1' ORDER BY c.sort_order, LCASE(cd.name)");
$category_data = array();
foreach ($query->rows as $row) {
$category_data[$row['parent_id'] ][$row['category_id'] ] = $row;
}
$this->cache->set('category.all.' . $this->config->get('config_language_id') . '.' . (int)$this->config->get('config_store_id'), $category_data);
}
return $category_data;
}
Метод возвращает массив всех узлов. Кадый узел это массив с ключом равным parent_id элементов массива. Все узлы и элементы в узлах отсортированы в соответствии с тем как задано в админке.Для построения многоуровневого списка надо обойти весь массив начиная от корня (узел 0) и при выводе каждой категории проверять наличие узла с ключом равным идентификатору категории. Если узел есть то выводим потомков, нету - выводим следующую категорию.
Для drop-down меню я в контроллере модуля категорий добавил свойство
protected $categories = array();и метод
protected function getAllCategories($parent_id = 0, $current_path = '') {
$output = '';
if (array_key_exists($parent_id, $this->categories)) {
$results = $this->categories[$parent_id];
$output .= '<ul>';
foreach ($results as $result) {
if (!$current_path) {
$new_path = $result['category_id'];
} else {
$new_path = $current_path . '_' . $result['category_id'];
}
$output .= '<li>';
$children = '';
if (array_key_exists($result['category_id'], $this->categories)) {
$children = $this->getAllCategories($result['category_id'], $new_path);
}
$href = $this->model_tool_seo_url->rewrite(HTTP_SERVER . 'index.php?route=product/category&path=' . $new_path);
// if ($this->category_id == $result['category_id']) { // будет помечена только активная категория
if (in_array($result['category_id'], $this->path)) { // будут помечены активная категория и все категории до корня
$output .= '<a class="active" href="' . $href . '">' . $result['name'] . '</a>';
} else {
$output .= '<a href="' . $href . '">' . $result['name'] . '</a>';
}
$output .= $children;
$output .= '</li>';
}
$output .= '</ul>';
}
return $output;
}
В методе index такие изменения
// $this->data['category'] = $this->getCategories(0);
$this->categories = $this->model_catalog_category->getAllCategories();
$this->data['category'] = $this->getAllCategories();
Всё... теперь при холодном старте только 1 запрос к БД, а не сотня как мне доводилось видеть.А если не надо отмечать активные категории, например для superfish, можно смело кешировать результат работы метода getAllCategories и вообще забыть о нагрузке от категорий.