Opencart Custom Frontend Module Development Tutorial

Want to create a more engaging user experience for your Opencart store? Our step-by-step tutorial will teach you how to build a custom frontend module.

In the previous tutorial, we learned how to make the custom module changes for the admin section, in this tutorial we'll see the changes required for the front end side.

This tutorial is the second part of my previous tutorial: How to make a custom module in opencart 2.x

The Structure of Opencart Module

There are mainly six to eight files that need to be created for any module. The following screenshot shows the hierarchy of files and folders of an OpenCart module: 

opencart folder structure

 

Creating the Front End side

Front End section will have three files:

  • Controller File
  • Language File
  • View File

Create a Controller File at path: catalog/controller/module/popular.php
Create a Language File at path: catalog/language/english/module/popular.php
Create a View File at path: catalog/view/template/module/popular.tpl

 

Controller File

Below is the complete controller file.

<?php
class ControllerModulePopular extends Controller {
 public function index($setting) {
  $this->load->language('module/popular');
  
  $data['heading_title']   = $this->language->get('heading_title');
  $data['text_tax']        = $this->language->get('text_tax');
  $data['button_cart']     = $this->language->get('button_cart');
  $data['button_wishlist'] = $this->language->get('button_wishlist');
  $data['button_compare']  = $this->language->get('button_compare');
  
  $this->load->model('catalog/product');
  $this->load->model('tool/image');
  
  $data['products'] = array();  
  $product_data     = array();
  
  if(isset($setting['limit']) && $setting['limit']!=''){
     $setting['limit'] = $setting['limit'];
  }
  else{
       $setting['limit'] = 4;
  }
  
  
  $query = $this->db->query("SELECT p.product_id FROM " . DB_PREFIX . "product p 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') . "' ORDER BY p.viewed DESC LIMIT " . (int)$setting['limit']);
  
  
  
  foreach ($query->rows as $result) {   
   $product_data[$result['product_id']] = $this->model_catalog_product->getProduct($result['product_id']);
  }
          
  $results = $product_data;
  
  if ($results) {
  foreach ($results as $result) {
   if ($result['image']) {
    $image = $this->model_tool_image->resize($result['image'], $setting['width'], $setting['height']);
   } else {
    $image = $this->model_tool_image->resize('placeholder.png', $setting['width'], $setting['height']);
   }
   
   
   if (($this->config->get('config_customer_price') && $this->customer->isLogged()) || !$this->config->get('config_customer_price')) {
    $price = $this->currency->format($this->tax->calculate($result['price'], $result['tax_class_id'], $this->config->get('config_tax')));
   } else {
    $price = false;
   }
     
   if ((float)$result['special']) {
    $special = $this->currency->format($this->tax->calculate($result['special'], $result['tax_class_id'], $this->config->get('config_tax')));
   } else {
    $special = false;
   } 
   
   if ($this->config->get('config_tax')) {
    $tax = $this->currency->format((float)$result['special'] ? $result['special'] : $result['price']);
   } else {
    $tax = false;
   }
   
   
   if ($this->config->get('config_review_status')) {
    $rating = $result['rating'];
   } else {
    $rating = false;
   }
       
   $data['products'][] = array(
    'product_id'   => $result['product_id'],
    'thumb'        => $image,
    'name'         => $result['name'],
    'description'  => utf8_substr(strip_tags(html_entity_decode($result['description'], ENT_QUOTES, 'UTF-8')), 0, $this->config->get('config_product_description_length')) . '..',
    'price'        => $price,
    'special'      => $special,
    'tax'          => $tax,
    'rating'       => $rating,
    'href'         => $this->url->link('product/product', 'product_id=' . $result['product_id']),
   );
  }

   if (file_exists(DIR_TEMPLATE . $this->config->get('config_template') . '/template/module/popular.tpl')) {
    return $this->load->view($this->config->get('config_template') . '/template/module/popular.tpl', $data);
   } else {
   return $this->load->view('default/template/module/popular.tpl', $data);
     }
     }
 }
}

 

The below line will load the language file of our custom module "Popular Products" so that we can access the language variables defined in the language file.

$this->load->language('module/popular');

 

Now we are fetching the language variables and setting them in the $data variable so that they can be accessed in the view template files.

$data['heading_title']   = $this->language->get('heading_title');
$data['text_tax']        = $this->language->get('text_tax');
$data['button_cart']     = $this->language->get('button_cart');
$data['button_wishlist'] = $this->language->get('button_wishlist');
$data['button_compare']  = $this->language->get('button_compare');

$this->load->model('catalog/product');
$this->load->model('tool/image');

 

The above lines will load the front end side product model so that we can use all the methods of it. The second one is used to fetch the product image.

if (isset($setting['limit']) && $setting['limit']!='') {
    $setting['limit'] = $setting['limit'];
}
else {
    $setting['limit'] = 4;
}

 

This is used to set the default limit of the product display. The below query is the heart of this module.

$query = $this->db->query("SELECT p.product_id FROM " . DB_PREFIX . "product p 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') . "' ORDER BY p.viewed DESC LIMIT " . (int)$setting['limit']);

 

Now we are getting the information about all the products fetched from the above query.

foreach ($query->rows as $result) {   
 $product_data[$result['product_id']] = $this->model_catalog_product->getProduct($result['product_id']);
}
$results = $product_data;

 

and then after we are loop through the $results array and fetch the product image, price, special price, tax, etc data and then we pass all these into product array so that they can be accessible in the view file.

$data['products'][] = array(
  'product_id'   => $result['product_id'],
  'thumb'        => $image,
  'name'         => $result['name'],
  'description'  => utf8_substr(strip_tags(html_entity_decode($result['description'], ENT_QUOTES, 'UTF-8')), 0, $this->config->get('config_product_description_length')) . '..',
  'price'        => $price,
  'special'      => $special,
  'tax'          => $tax,
  'rating'       => $rating,
  'href'         => $this->url->link('product/product', 'product_id=' . $result['product_id']),
 );

 

At last, we are checking the existence of the template file and loading the template file for this custom module.

if (file_exists(DIR_TEMPLATE . $this->config->get('config_template') . '/template/module/popular.tpl')) {
 return $this->load->view($this->config->get('config_template') . '/template/module/popular.tpl', $data);
} else {
 return $this->load->view('default/template/module/popular.tpl', $data);
}

 

Language file

As described, the language file will contain the language variables which describe what will be displayed in the view file. In language file constant name is used which never changes, only the values according to the language will be changed. If the currently selected language is other than English then the variables will be loaded from that language’s folder file.

<?php
// Heading
$_['heading_title'] = 'Popular Products';

// Text
$_['text_reviews']  = 'Based on %s reviews.';

// Tax
$_['text_tax']      = 'Ex Tax:';

 

View File

View file refers to the template file having .tpl extension. It will display all the set variables of the controller. In the view file, we loop through the $products array and display the products.

<h3><?php echo $heading_title; ?></h3>
<div class="row">
  <?php foreach ($products as $product) { ?>
  <div class="col-lg-3 col-md-3 col-sm-6 col-xs-12">
    <div class="product-thumb transition">
      <div class="image"><a href="<?php echo $product['href']; ?>"><img src="<?php echo $product['thumb']; ?>" alt="<?php echo $product['name']; ?>" title="<?php echo $product['name']; ?>" class="img-responsive" /></a></div>
      <div class="caption">
        <h4><a href="<?php echo $product['href']; ?>"><?php echo $product['name']; ?></a></h4>
        <p><?php echo $product['description']; ?></p>
        <?php if ($product['rating']) { ?>
        <div class="rating">
          <?php for ($i = 1; $i <= 5; $i++) { ?>
          <?php if ($product['rating'] < $i) { ?>
          <span class="fa fa-stack"><i class="fa fa-star-o fa-stack-2x"></i></span>
          <?php } else { ?>
          <span class="fa fa-stack"><i class="fa fa-star fa-stack-2x"></i><i class="fa fa-star-o fa-stack-2x"></i></span>
          <?php } ?>
          <?php } ?>
        </div>
        <?php } ?>
        <?php if ($product['price']) { ?>
        <p class="price">
          <?php if (!$product['special']) { ?>
          <?php echo $product['price']; ?>
          <?php } else { ?>
          <span class="price-new"><?php echo $product['special']; ?></span> <span class="price-old"><?php echo $product['price']; ?></span>
          <?php } ?>
          <?php if ($product['tax']) { ?>
          <span class="price-tax"><?php echo $text_tax; ?> <?php echo $product['tax']; ?></span>
          <?php } ?>
        </p>
        <?php } ?>
      </div>
      <div class="button-group">
        <button type="button" onclick="cart.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-shopping-cart"></i> <span class="hidden-xs hidden-sm hidden-md"><?php echo $button_cart; ?></span></button>
        <button type="button" data-toggle="tooltip" title="<?php echo $button_wishlist; ?>" onclick="wishlist.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-heart"></i></button>
        <button type="button" data-toggle="tooltip" title="<?php echo $button_compare; ?>" onclick="compare.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-exchange"></i></button>
      </div>
    </div>
  </div>
  <?php } ?>
</div>

 

In this part of the article, we completed the front end side of our Opencart custom module Popular products. It’s easy to modify the OpenCart modules if you know the basic core concepts of MVC.

Download Free Extension