Opencart Tutorial: Make the Actions Block Sticky in Admin Section

By


This is a very basic Opencart tutorial for beginners explaining one current issue in admin section and its simple solution. Even though its very easy and basic tutorial but it will help you to save time and improve admin user interaction while performing various actions. So lets see what is the actual issue and what could be the better solution for that.



Current Problem


In admin section, the action block(add/save/delete) at top right corner is not sticky so after filling the form values or selecting the check boxes in the records listing page you have to manually scroll to top in order to perform the action(save/edit/delete) which is normal behavior and fine if we have small amount of records on the page or the height of the form being editing is small.



Possible Solutions


There can be two solution to overcome this behavior, and there are as below:

  1. We can add the "scroll to top" functionality by adding the separate js for the same which will add one arrow button at the bottom right corner, by pressing this button the page will auto scroll to top. At certain level this option is ok, but if we think in better way then its not a recommended solution as we have to add extra files (css, javascript, images) in order to add this functionality.
  2. We can make some changes in the style classes to make the action bar sticky so even if we are at the bottom of the page, we have the action bar sticked at its place!! ohh, nice and easy solution. don't you like it?






So obviously we would prefer easy and better option. To make the action block sticky we need to add some css class. Simply follow the instruction given in the below section.

Open this file: root/admin/view/stylesheet/stylesheet.css and add the below class at the end of file.

/*css for sticky action buttons in admin area START*/
.buttons, .page-header .pull-right{
    background: #FFF;
    border: 1px solid #DDD;
    border-radius: 7px;
    box-shadow: 0 3px 6px #999;
    margin: -1px 0 0 !important;
    padding: 6px;
    position: fixed;
    right: 30px;
    z-index: 9999;
}
/*css for sticky action buttons in admin area END*/


That's it!! You're done. Go to the admin section and open any of the listing/editing page and Clear the cache (press Ctrl+F5) and see the result. Now scroll down the page, edit the form or insert new record the action block always there for you sticked!! :)




This changes are tested in Opencart Versions: 2.0.1.1 , 2.0.3.1 


If you find this tutorial helpful then please do share in your social network and dont forget to provide your reply in the below comment box.

Custom Product Field in Custom Theme Opencart

By
In any project you may need some kind of customization because it's not required that everyone satisfied with the default behaviour of the functionalities provided by the software/source. Regarding the opencart custom product fields, I have already written one series on the same topic. It was for the default theme and as I said, if you are having custom theme then you need to made some other changes to get the things working.




Introduction to Smarty Template Engine for Absolute Beginners

By
This tutorial is mainly for the absolute beginners, it's all about the basic introduction of the Smarty Template Engine.


What is Smarty?

Smarty is a template engine (or framework) for PHP. It provides a feasible way to separate application business logic and content data from the presentation. This would be best beneficial in a situation where the programmer and the template designer playing different roles.

Smarty 3.x versions requires a web server running PHP 5.2 or greater.
Smarty 2.x versions requires a web server running PHP 4 or 5.

Installation Process

  • Download Smarty Template Engine from here
  • Extract the downloaded package on your server.


Smarty File Structure

The below section displays the default structure of the Smarty. 
Smarty Default Structure


Above file structure is the default one that comes with the fresh Smarty Package. In order to work with Smarty you need to create some more folders required by Smarty.


Smarty requires four directories to be created which are by default named as templates/, templates_c/, configs/ and cache/. These directories are defined by the Smarty class properties $template_dir, $compile_dir, $config_dir, and $cache_dir respectively.


Properties Description



$template_dir - Default template directory. By default this is ./templates. Smarty will check for the templates/ directory in the same directory as the executing php script.


$compile_dir  - Directory where compiled templates are located. By default this is ./ templates_c. Smarty will check for the templates_c/ directory in the same directory as the executing php script. Make sure that this directory is writeable by the web server.


$cache_dir    -  Directory where template caches are stored. By default this is ./cache. Smarty will check for the cache/ directory in the same directory as the executing php script. Make sure that this directory is writeable by the web server.



Making Simple Hello User Application


Now we are going to make simple Hello World application using Smarty Template Framework. Here we are making just demo application called "smarty_demo".

Make one directory called 'lib' in our application root and copy paste the content of the fresh Smarty package's "libs" folder into this newly created "lib" folder.

Let's make one file called index.php in the root of application and make one file called index.tpl inside the templates directory. As I mentioned above four new directories are also required in Smarty. So make the following directories inside the root of our application.

cache  
templates  
templates_c  
configs

Check the below image, you will have better idea about the final file structure.



Smarty uses a PHP constant named SMARTY_DIR which is the full system file path to the Smarty
libs/ directory. Basically, if your application can find the Smarty.class.php file, you do not need to set the SMARTY_DIR as Smarty will figure it out on its own. Therefore, if Smarty.class.php is not in your include_path, or you do not supply an absolute path to it in your application, then you must define SMARTY_DIR manually. SMARTY_DIR must include a trailing slash/.

Now open index.php and add the below code:

<?php  
require('libs/Smarty.class.php');  
$smarty = new Smarty;  
$smarty->caching = true;  
$smarty->cache_lifetime = 120;  
$smarty->assign("name", "Guest User");    
$smarty->display('index.tpl');  
?>


Now open index.tpl and add the below code:
Hello {$name}, welcome to Smarty!


Point your browser to this smarty_demo application and it will print below message:

Hello Guest User, welcome to Smarty!



If you face an error saying the Smarty.class.php file could not be found, then visit the below link, it
might be helpful:



If you get the above message then enjoy.!! You have correctly installed the Smarty and done with simple Hello User application. We will see other possible aspects of the Smarty in detail in next tutorials.


Features of Smarty


  1. Its easy to learn.
  2. Its very fast.
  3. One time compilation, no template parsing overhead
  4. Really very Smart! (recompile only changed template files).
  5. Scalable for large applications and high traffic websites
  6. Provides built in caching support
  7. Allow configuration of template {delimiter} tag syntax. and many more...

Conclusion


This article is dedicated to absolute beginners. A lot more things can be done (or possible) with Smarty that I can't write down all of them in single post. I hope this should give you very clear basic idea to start with it. So let's give a try for the amazing "Smarty Templating Framework".

Product Options Validation in Opencart Part: 2

By
Product Options Validation in Opencart



This tutorial is the second part of my previous post Product Option Validation in Opencart . In this tutorial we are going to cover the changes required to add the validation in the font end side. Before moving ahead, make sure that you read previous article and done the changes required for the admin section.


Here we are going to add two validation rules for the text product options:

Rule 1 : Restrict the numbers of characters
Rule 2 : Match the entered value with your pattern (regex)


Step : 1



Open file /catalog/controller/checkout/cart.php and search for the below code for the product options foreach loop: (approx line no:309)

foreach ($product_options as $product_option) { 
 

now add the below code just after the first if condition(for the required field) inside the above loop,

/*custom validation code start*/
    else if ($product_option['validationrule']=='13' && $option[$product_option['product_option_id']]!='') {
 $check = $this->validateForm($option[$product_option['product_option_id']],13);
     if ($check=='13') {
   $json['error']['option'][$product_option['product_option_id']] = sprintf($this->language->get('error_13max'), $product_option['name']);
     } else if ($check=='0') {
   $json['error']['option'][$product_option['product_option_id']] = sprintf($this->language->get('error_special'), $product_option['name']);
      }
     }
/*custom validation code end*/


Here, note that we have used one custom validation function called validateForm(). So you need to add the below function at last in this file inside the main class. The regex is just for the demo test, you can add any rule as per your requirement(if required). The second inside condition is for checking the length of the input characters.

/*added new function for the regex validation*/
protected function validateForm($field,$maxlength) {
 if ($field!='') {
  if(preg_match("/^[A-Za-z0-9 \.\~\,\/\-\']+$/", $field)){
   if ((utf8_strlen($field) > $maxlength)) {
    return $maxlength;
   }
  } else {
   return 0;
  } 
 }
}

Step : 2


Open /catalog/model/catalog/product.php file and search for the below code inside the $product_option_data[] array (approx line no : 348)

'required'             => $product_option['required']

and add the below line just after that. 

'validationrule'       => $product_option['validationrule']


make sure to add ',' after required option :

'required'             => $product_option['required'],
'validationrule'       => $product_option['validationrule']


Step : 3


Open /catalog/language/english/checkout/cart.php file and search for the below line:


$_['error_required']           = '%s required!';

add the below lines of code just after that:

$_['error_special']          = 'Please enter only allowed characters for %s!';
$_['error_13max']            = 'Max 13 characters are allowed!';




That's it..!! You're done with the Front end section. Now create new product(or modify existing) and add the custom validation rule from the admin section and then go to that product in front end side. 

Click add to cart with following cases:


  • Without filling the value
  • Adding not allowed characters (as per your regex set in the function)
  • Adding characters more than 13


Check everything is working as expected or not. If you face any error or having any trouble with the implementation than make sure that you followed both of the articles for the product options validation correctly and even though you face any issue then feel free to comment in the below comment box, I will help you to sort it out your issues.



Scope of enhancement and modifications


The concept presented here is very basic and just to show how you can add your extra validation rule in opencart with the only default required option. In real application or scenario you might need to modify this code to fulfill your requirements. Below are some of cases from my side that can be included in this concept.


  1. You can multiple validation rules
  2. You can fire validation rule for specific categories only
  3. You can add validation rules for all the possible options (like radio, checkbox etc.)
  4. You can add multiple and more complex regex in the validation
  5. You can have conditional hierarchy of the rules also.


All these points are depended upon the requirements you have. I just provided the way how it can be accomplished. Now its upto you, how you utilize it.. :)

Product Options Validation in Opencart

By
Product Options Validation in Opencart



Opencart is very popular e-commerce platform. Each and every platform has their own pre-defined set of rules and functionalities. Such functionalities are useful to fulfil the requirements of any online ecommerce store.

Displaying Custom Product Field on Front End in Opencart

By
In my one of previous tutorial we learned about "Adding Custom Product Field in Opencart".This tutorial will explain how to display the custom product field on front end. 

Note: Before making any changes directly at any levels , its recommended to take a Backup of database and files.

All the changes explained in this tutorial is for the default theme and English language. If you are using custom theme then some extra changes are required.If you're running a store in multi language , then you need to download the respective language pack from the opencart extension store and need to add the changes in that pack also.


So before starting, we need to list out the pages where we need to display the custom product field on front end. Below are some of the pages at front end in which we are going to make the changes to display the custom product field.


  1. Product Detail page
  2. Products Compare Page
  3. Products Search Page



Product Detail Page


As the name suggests, Product detail page has all the information about the product. If you want to display the custom_desc field then make the following changes:

(1) Open catalog/language/english/product/product.php file and add the below line :

$_['text_custom_desc']         = 'Custom Description:';

(2) Open catalog/model/catalog/product.php file and search for the below code in the getProduct() function:

'isbn'             => $query->row['isbn'],

and add the below code just after that :

'custom_desc'      => $query->row['custom_desc'],

(3) Open catalog/controller/product/product.php file and search for the below code:

$data['text_loading'] = $this->language->get('text_loading');

and add the below line just after that:

$data['text_custom_desc'] = $this->language->get('text_custom_desc');

Now search for the below line:

$data['entry_bad'] = $this->language->get('entry_bad');

and add the below line just after that:

$data['entry_custom_desc'] = $this->language->get('entry_custom_desc');

Search for the below line :

$data['description'] = html_entity_decode($product_info['description'], ENT_QUOTES, 'UTF-8');

In case if you use ckeditor for adding new "custom_desc" value in the admin side, then add the following code just after that.

$data['custom_desc'] = html_entity_decode($product_info['custom_desc'], ENT_QUOTES, 'UTF-8');

otherwise you can simply add as below,

$data['custom_desc'] = $product_info['custom_desc'];

Now search for the below line:

'special'     => $special,

and add the below code just after that:

'custom_desc' => $result['custom_desc'],

(4) Open catalog/view/theme/default/template/product/product.tpl file. Now here we have more that one choices.

-- If you want to display the custom_desc inside the description tab, then find the below line

<div class="tab-pane active" id="tab-description"><?php echo $description; ?>

and add the following code just after that (inside the same div).

<?php echo $custom_desc;?>

-- If you want to display the custom_desc just after the product title, then find the below line:

<h1><?php echo $heading_title; ?></h1>

and add the below line just after that:

<?php echo $text_custom_desc; ?> <?php echo $custom_desc;?>

-- If you want to display the custom_desc in the Related Products block , then search for the below line :

<p><?php echo $product['description']; ?></p>

and add this just after that:

<p><?php echo $product['custom_desc']; ?></p>


Products Search Page


Opencart provides the searching functionality to easily find the product the user want to search in the store. If you want to display the custom_desc field display in the search page then do the following changes:


(1) Open catalog/controller/product/search.php file and search for the below line in the $data['products'][] array:

'price'      => $price,

and add the below line just after that:

'custom_desc' => $result['custom_desc'],


(2) Open catalog/view/theme/default/template/product/search.tpl file and search for the below line :

<p><?php echo $product['description']; ?></p>

and add the below line after that :

<p><?php echo $product['custom_desc']; ?></p>



Product Compare Page


Opencart provides the functionality of comparison between the products so that users can view the features of the different products side by side. In order to add the custom_desc field in the product compare page make the below changes.


(1) Open catalog/language/english/product/compare.php file and add the below code:

$_['text_custom_desc']  = 'Custom Description';


(2) Open catalog/controller/product/compare.php file and find the below code:

'model'        => $product_info['model'],

and add the below line of code just after that:

'custom_desc'  => $product_info['custom_desc'],

Now find,

$data['text_summary'] = $this->language->get('text_summary');

and add the below code just after that:

$data['text_custom_desc'] = $this->language->get('text_custom_desc');


(3) Open catalog/view/theme/default/template/product/compare.tpl file and search for the below code:

<tr>
    <td><?php echo $text_summary; ?></td>
    <?php foreach ($products as $product) { ?>
    <td class="description"><?php echo $products[$product['product_id']]['description']; ?></td>
    <?php } ?>
</tr>

and add the below code just after that:

<tr>
    <td><?php echo $text_custom_desc; ?></td>
    <?php foreach ($products as $product) { ?>
    <td class="description"><?php echo $products[$product['product_id']]['custom_desc']; ?></td>
    <?php } ?>
</tr>


That's it..!! You have custom_desc field display in front end. Let me know if you get any error , issues or want to display custom_desc in any other section of the front end side. I will add the code for the same as an update of this article.




Latest Orders Module for Opencart

By

Nowadays online shopping is very popular. There are too many online stores available selling variety of products. So in this competition in order to stay in market, you need to present something one step advanced than others.


If we talk about the online store then its design, products presentation, navigations etc., matters a lot which makes easy for the users to find their desired products that they wants to buy.


If you display the section of "Latest Orders" then its possible to win the trust of the potential customers. This section display the recent orders made by others. There are more chances that the visitor will click on the latest ordered products and may buy that product.



So, if you are running a online store and searching for the "Latest Orders Module in Opencart" then there is one good news for you. The latest module for the latest orders in opencart is available for the new versions.



Module Name          : Latest Orders
Versions                   : 2.0.0.0 ,2.0.1.0 and 2.0.1.1
Price                      : $7


If you want to buy the module then you can contact me on my email id : ketan32.patel@gmail.com
with subject "Opencart Module Inquiry". I will provide further details via email.

I accept the payment through Paypal.


You can check others FREE Modules developed by me:

Module Name             : Popular Products in Opencart
Versions                      : 2.0.0.0, 2.0.1.0 and 2.0.1.1
Price                         : FREE
Link                         : Download


Module Name            : Top Sales in Opencart
Versions                     : 2.0.0.0, 2.0.1.0 and 2.0.1.1
Price                         : FREE
Link                            : Download



Opencart Custom Module Development : Part 2

By
custom module development


This tutorial is the second part of my previous tutorial : How to make custom module in opencart 2.x. In 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. 

The Structure of Opencart Module


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




Creating the Front End side


Front End section will have three files:
  • Controller File
  • Language File
  • View File


Create a Controller File 
Path: catalog/controller/module/popular.php

Create a Language File 
Path: catalog/language/english/module/popular.php

Create a View File 
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');

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 of 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, language file will contains the language variables which describes what will be displayed in view file. In language file constant name is used which never changes, only the values according to the language will be changed.

If the current 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 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 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 Module