How to list all categories and sub-categories in the header in OpenCart

In an earlier post I detailed how to list categories in the header in OpenCart without changing any of the source files. I’ve now extended the script to list sub-categories.

Basically, the original script looped through all the top-level categories and put them in an unordered list. In this new script I’ve just repeated the loop within each top-level category. It loops through all the sub-categories of the top-level category (if there are any) and puts them in an unordered list too.

Please note that it will only output the top-level, and second-level categories. If you have third- and fourth-level categories you will need to repeat the loop again within each sub-category in order to list them. But, honestly, if you have third- and fourth-level categories in your shop I’d think about restructuring how you’ve ordered it. :-)

Edit: Shahz, in the comments below, has written a version that lists the third level. You can find it here:

Thanks Shahz.

Anyway, here’s the code:

$results = $this->model_catalog_category->getCategories();
if ($results) {$output = '<ul>';}
foreach ($results as $result) {
$output .= '<li>';
$new_path = $result['category_id'];
$unrewritten = HTTP_SERVER.'index.php?route=product/category&path=' . $new_path;
$rewritten = $this->model_tool_seo_url->rewrite($unrewritten);
$output .= '<a href="' . $rewritten . '">' . $result['name'] . '</a>';
$sub_results = $this->model_catalog_category->getCategories($new_path);
if ($sub_results) {$output .= '<ul>';}
foreach ($sub_results as $sub_result) {
$output .= '<li>';
$new_sub_path = $sub_result['category_id'];
$sub_unrewritten = $unrewritten.'_'. $new_sub_path;
$sub_rewritten = $this->model_tool_seo_url->rewrite($sub_unrewritten);
$output .= '<a href="' . $sub_rewritten . '">' . $sub_result['name'] . '</a>';
$output .= '</li>';
if ($sub_results) {$output .= '</ul>';}
$output .= '</li>';
if ($results) {$output .= '</ul>';}
echo $output;

This entry was posted in OpenCart Mods. Bookmark the permalink.

21 Responses to How to list all categories and sub-categories in the header in OpenCart

  1. Pingback: Useful OpenCart Links and Articles | Heather Buchel

  2. arjan says:

    should i just paste the code into the header.tpl ?

    Thanx for the help

  3. Craig says:

    Hi arjan, that’s all you need to do. It’s as simple as that.

    I hope you’re not trying to spam me, cos your website says you’re an experienced web developer. Doesn’t really matter to me as the link is nofollow anyway, so I’ll let it go just incase you’re genuine. In which case, I’m glad to help. Look forward to seeing this in your new templates.

  4. arjan says:


    thanks for your help and your compliment ;-)
    I do not see myself as an experienced webdeveloper. I discovered Opencart just a few weeks ago. My next theme will have the (sub)categories in het navigationbar, i’ll promise.

  5. Pingback: How to highlight current category in the header in OpenCart | DIY E-Commerce

  6. Gareth says:

    Craig makes OpenCart templating fun ;)

    Quick question though:

    I wanted to list products that are for a particular category.

    I created an IF statement, inside your for each results loop and thought I would try to output products there, however this lists all products for every category and not just the one mentioned in the IF.

    Have you come across an easy way to do this? Output products per category?


  7. Craig says:

    Cheers Gareth, I should quote you in the strapline for my site!

    I’ve not tried listing products for a particular category but there is a function called getProductsByCategoryId(). You could try the following for the main category:
    For subcategories try:

    I imagine you’re trying to build a mega-dropdown. Hope that works. Let me know.

  8. Gareth says:

    Thanks Craig, finding that function was key!

    Actually, I wasn’t after a mega nav, I simply wanted to list the latest products on my home page and list them in order of each category.

    I simply just started playing around with my latest_home.tpl file and managed to get the output I needed. I had to take a lot of functionality from the latest.php but got there in the end.

    I haven’t loaded any models because this was already done in the latest.php file but you could potentially take what I’ve done and make it work independently of any other files.

    Here is my code (you will notice I am no PHP developer, I prefer to play with the front end):

    $category_ids = array(39,38,36,42);
    foreach ($category_ids as $category_id) {
    $category_info = $this->model_catalog_category->getCategory($category_id);
    $products = $this->model_catalog_product->getProductsByCategoryId($category_id);
    $limit = $this->config->get('latest_limit');
    if($limit > sizeof($products)) {
    $trim = sizeof($products);
    } else {
    $trim = $limit;
    if ($category_info) {
    $heading_title = $category_info['name'];
    <!-- HTML Output for item list -->
    <div class="recent_list">
    <h1><?php echo $heading_title; ?></h1>
    <ul class="items">
    for ($i = 0; $i < $trim; $i++) {
    $title = $products[$i]['name'];
    $thumb = $this->model_tool_image->resize($products[$i]['image'], $this->config->get('config_image_product_width'), $this->config->get('config_image_product_height'));
    $special = FALSE;
    $discount = $this->model_catalog_product->getProductDiscount($products[$i]['product_id']);
    if ($discount) {
    $price = $this->currency->format($this->tax->calculate($discount, $products[$i]['tax_class_id'], $this->config->get('config_tax')));
    } else {
    $price = $this->currency->format($this->tax->calculate($products[$i]['price'], $products[$i]['tax_class_id'], $this->config->get('config_tax')));
    $special = $this->model_catalog_product->getProductSpecial($products[$i]['product_id']);
    if ($special) {
    $special = $this->currency->format($this->tax->calculate($special, $products[$i]['tax_class_id'], $this->config->get('config_tax')));
    if ($this->config->get('config_review')) {
    $rating = $this->model_catalog_review->getAverageRating($products[$i]['product_id']);
    } else {
    $rating = false;
    $stars = sprintf($this->language->get('text_stars'), $rating);
    $options = $this->model_catalog_product->getProductOptions($products[$i]['product_id']);
    if ($options) {
    $add = $this->model_tool_seo_url->rewrite(HTTP_SERVER . 'index.php?route=product/product&amp;product_id=' . $products[$i]['product_id']);
    } else {
    $add = HTTPS_SERVER . 'index.php?route=checkout/cart&amp;product_id=' . $products[$i]['product_id'];
    <?php if($i % 4 == 0){ ?>
    <li class="nospace">
    <?php } else {?>
    <?php }?>
    <img src="<?php echo $thumb; ?>" />
    <h5><?php echo $title; ?></h5>
    <p class="price">
    <?php if (!$special) { ?>
    <?php echo $price; ?>
    <?php } else { ?>
    <span class="strike"><?php echo $price; ?></span> <?php echo $special; ?>
    <?php } ?>
    <?php if ($rating) { ?>
    <img src="catalog/view/theme/default/image/stars_<?php echo $rating . '.png'; ?>" alt="<?php echo $stars; ?>" />
    <?php } ?>
    <p class="add_to_cart">
    <a href="<?php echo $add; ?>">Add To Cart</a>
    <?php } ?>
    <?php } ?>

    I have hard coded the category Ids I want to list (as I wanted strict control), however, you could generate an array from your getCategories function above, or edit the admin template to allow the user to specify.

    Sorry for high jacking your comments, just thought I would post in case there is someone else out there looking to achieve something similar!

    Feel free to tidy up my awful code ;)



  9. Craig says:

    Hi Gareth, glad to be of help, even if I misunderstood. I’m not a PHP programmer either. Just like you I’d rather deal with the front end, but sometimes, just like owning a car, you have to look under the bonnet and get your hands dirty. Code seems fine to me from scanning through it (have sorted the pasteing issue – WordPress is rubbish for code). Thanks for posting.

  10. Ted says:

    Great posts! What do I need to do in order to add Manufacturers or brands instead of categories?

  11. Craig says:

    Hi Ted,

    I’ve not tested this but you’ll need to use this function instead:

    $results = $this->model_catalog_manufacturer->getManufacturers();

    You then use ‘manufacturer_id’ instead of ‘category_id’.

    Hope that works,


  12. kevindrops says:

    Hi thanks for this top menu code
    its displaying vertically same s it did in category part
    and also displays subcategory under it

    is it possible to display it horizontally ?
    and submenu pops out when main category is clicked

    i am using 1.48 version i cant change version now since i have done lots of editing in my code ..

    What I have done. I just copied cut paste it in my default templates common header .tpl

    just after line 71 after this code

    <a href="” id=”tab_logout”>

    <a href="” id=”tab_account”><a href="” id=”tab_cart”><a href="” id=”tab_checkout”>

    when it was not displaying properly… i added div tag around it open and close

    here is the opening part

    but it didnt help

    where did i go wrong? pls guide.

  13. Craig says:

    Hi Kevin, you’re going to need to use styles in the stylesheet to create a dropdown menu from the list of categories. A great place to start, and where most people do, is Suckerfish Dropdowns on Alistapart here:

  14. Pingback: OpenCart header navigation (three level list) | ShahZ

  15. Shahz says:

    Hey Craig,

    Thanks for the great piece of code. I have extended your code to add the third level navigation too. I think the third level is still fine, but fourth level makes no sense :)

    You can add my link after your code in case someone might need it. People make awesome open source applications. But its people like you take them to a new level. Cheers!

    Open Source ftw!

  16. adam says:

    Hi Craig, your code is very useful to me
    i combine your code with suckerfish vertical drop down menu and it work

    thank you so much for your great article

  17. nady says:

    for validation <a href="'.str_replace('&', '&amp;', $rewritten).'">

  18. Craig says:

    Thanks nady

  19. Igan says:

    I;ve tried garreth code but it didn;t worked out..only blanked

  20. Mark says:

    Thankyou for sharing, here it is adapted for a jump menu:

    $results = $this->model_catalog_category->getCategories();

    foreach ($results as $result) {

    $new_path = $result['category_id'];
    $unrewritten = HTTP_SERVER.'index.php?route=product/category&path=' . $new_path;
    $rewritten = $this->model_tool_seo_url->rewrite($unrewritten);
    $output .= '' . $result['name'] . '';
    $sub_results = $this->model_catalog_category->getCategories($new_path);

    foreach ($sub_results as $sub_result) {

    $new_sub_path = $sub_result['category_id'];
    $sub_unrewritten = $unrewritten.'_'. $new_sub_path;
    $sub_rewritten = $this->model_tool_seo_url->rewrite($sub_unrewritten);
    $output .= '   ' . $sub_result['name'] . '';
    echo $output;

    Sorry for double post (html was first stripped out)

  21. Craig says:

    Hi Igan, did you try changing the numbers in the array at the start – they need to be the IDs of your categories.

Leave a Reply

Your email address will not be published. Required fields are marked *


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>