Example: selecting the main category "Hard Drives" should list all hard drives regardless of subcategorisation. Selecting the subcategory "SSD Hard Drives" should show all SSD hard drives, and so on.
This isn't supported in the core OpenCart system (you have to navigate to the explicitly assigned subcategory to see the product), but it's not so hard to add some nested set (modified pre-order tree traversal) functionality to make this easy and resource-efficient to do.
NOTE: BACK UP YOUR OPENCART INSTALLATION AND DATABASE BEFORE ATTEMPTING THESE CHANGES!
Step 1: Add two Int(11) fields to the [DB_PREFIX]category table named "lft" and "rgt"
Step 2: In "admin/model/catalog/category.php", add the following recursive function directly below the first line ("class ModelCatalogCategory extends Model {"):
Code: Select all
private function rebuildTree($parent, $left) {
// the right value of this node is the left value + 1
$right = $left+1;
// get all children of this node
$query = $this->db->query('SELECT category_id FROM ' . DB_PREFIX . 'category WHERE parent_id="'.$parent.'";');
foreach ($query->rows as $result) {
// recursive execution of this function for each
// child of this node
// $right is the current right value, which is
// incremented by the rebuild_tree function
$right = $this->rebuildTree($result['category_id'], $right);
}
// we've got the left value, and now that we've processed
// the children of this node we also know the right value
// Since the root category is virtual (not represented in the database), we skip it
if ($left>1) {
$this->db->query('UPDATE ' . DB_PREFIX . 'category SET lft='.$left.', rgt='.$right.' WHERE category_id="'.$parent.'";');
}
// return the right value of this node + 1
return $right+1;
}
Code: Select all
$this->rebuildTree(0,1);
Step 5: In the file "catalog/model/catalog/product.php", find the function called getProductsByCategoryId.
You should replace the first line which looks like this:
$sql = "SELECT *, pd.name AS name, p.image, m.name AS manufacturer, ss.name AS stock, (SELECT AVG(r.rating) FROM " . DB_PREFIX . "review r WHERE p.product_id = r.product_id GROUP BY r.product_id) AS rating FROM " . DB_PREFIX . "product p LEFT JOIN " . DB_PREFIX . "product_description pd ON (p.product_id = pd.product_id) LEFT JOIN " . DB_PREFIX . "product_to_store p2s ON (p.product_id = p2s.product_id) LEFT JOIN " . DB_PREFIX . "manufacturer m ON (p.manufacturer_id = m.manufacturer_id) LEFT JOIN " . DB_PREFIX . "stock_status ss ON (p.stock_status_id = ss.stock_status_id) LEFT JOIN " . DB_PREFIX . "product_to_category p2c ON (p.product_id = p2c.product_id) WHERE p.status = '1' AND p.date_available <= NOW() AND pd.language_id = '" . (int)$this->config->get('config_language_id') . "' AND p2s.store_id = '" . (int)$this->config->get('config_store_id') . "' AND ss.language_id = '" . (int)$this->config->get('config_language_id') . "' AND p2c.category_id = '" . (int)$category_id . "'";
With the following:
Code: Select all
"$sql = "SELECT *, pd.name AS name, p.image, m.name AS manufacturer, ss.name AS stock, (SELECT AVG(r.rating) FROM " . DB_PREFIX . "review r WHERE p.product_id = r.product_id GROUP BY r.product_id) AS rating FROM " . DB_PREFIX . "product p LEFT JOIN " . DB_PREFIX . "product_description pd ON (p.product_id = pd.product_id) LEFT JOIN " . DB_PREFIX . "product_to_store p2s ON (p.product_id = p2s.product_id) LEFT JOIN " . DB_PREFIX . "manufacturer m ON (p.manufacturer_id = m.manufacturer_id) LEFT JOIN " . DB_PREFIX . "stock_status ss ON (p.stock_status_id = ss.stock_status_id) LEFT JOIN " . DB_PREFIX . "product_to_category p2c ON (p.product_id = p2c.product_id) WHERE p.status = '1' AND p.date_available <= NOW() AND pd.language_id = '" . (int)$this->config->get('config_language_id') . "' AND p2s.store_id = '" . (int)$this->config->get('config_store_id') . "' AND ss.language_id = '" . (int)$this->config->get('config_language_id') . "' AND p2c.category_id IN (SELECT category_id FROM " . DB_PREFIX . "category c1 WHERE c1.lft BETWEEN (SELECT lft from " . DB_PREFIX . "category c2 WHERE c2.category_id='" . (int)$category_id . "') AND (SELECT rgt from " . DB_PREFIX . "category c3 WHERE c3.category_id='" . (int)$category_id . "'))";"
$query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "product_to_category p2c LEFT JOIN " . DB_PREFIX . "product p ON (p2c.product_id = p.product_id) LEFT JOIN " . DB_PREFIX . "product_to_store p2s ON (p.product_id = p2s.product_id) WHERE p.status = '1' AND p.date_available <= NOW() AND p2s.store_id = '" . (int)$this->config->get('config_store_id') . "' AND p2c.category_id = '" . (int)$category_id . "'");
with the following:
Code: Select all
$query = $this->db->query("SELECT COUNT(*) AS total FROM " . DB_PREFIX . "product_to_category p2c LEFT JOIN " . DB_PREFIX . "product p ON (p2c.product_id = p.product_id) LEFT JOIN " . DB_PREFIX . "product_to_store p2s ON (p.product_id = p2s.product_id) WHERE p.status = '1' AND p.date_available <= NOW() AND p2s.store_id = '" . (int)$this->config->get('config_store_id') . "' AND p2c.category_id IN (SELECT category_id FROM " . DB_PREFIX . "category c1 WHERE c1.lft BETWEEN (SELECT lft from " . DB_PREFIX . "category c2 WHERE c2.category_id='" . (int)$category_id . "') AND (SELECT rgt from " . DB_PREFIX . "category c3 WHERE c3.category_id='" . (int)$category_id . "'))");
I've had some help from the following resources to put this together:
http://articles.sitepoint.com/print/hie ... a-database
http://dev.mysql.com/tech-resources/art ... -data.html