Feb 28, 2015

Ketan Patel

Opencart Custom Module Development

custom module development


OpenCart is free open source ecommerce platform for online merchants. OpenCart provides a professional and reliable foundation from which to build a successful online store. This foundation appeals to a wide variety of users; ranging from seasoned web developers looking for a user-friendly interface to use, to shop owners just launching their business online for the first time. OpenCart has an extensive amount of features that gives you a strong hold over the customization of your store. With OpenCart tools, you can help your online shop live up to its fullest potential.   - Opencart

Opencart custom module development is a core part of any Opencart project, because at any stage of project you may want to add your own custom functionality(module) in your existing Opencart package.


In this tutorial we’ll be talking about creating a Opencart module that displays Popular Products from database. We will start by describing the file structure of a module and where it must be included in the Opencart structure, then we’ll be creating a configuration panel for our module to allow the site admin to customize it to his/her requirements. Next we’ll be having a closer look at how to make our module visible for Opencart frontend.

Throughout this series, I’m referring Opencart latest version 2.0.1.1 , though custom module structures are the same in all 2.x versions of Opencart. With minor modifications in code, you can develop the custom module for opencart 2.0.0.0 version. Before starting the actual module development, first understand the basic structure of Opencart module.

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:



As you can see in the image, the module file structure is having two parts : admin and catalog. The admin section folders and files deals with the module settings and data handling from the backend, while the catalog section folders and files responsible for displaying data in the frontend.

Creating the Admin Section

Admin section will have three files
  • Controller File
  • Language File
  • View File
Create a Controller File
Path: admin/controller/module/popular.php
Create a Language File
Path: admin/language/english/module/popular.php
Create a View File
Path: admin/view/template/module/popular.tpl

Controller File


In OpenCart a Controller is a class file that is named in a way that can be associated with a URL. For example note the below URL :
http://sitename.com/index.php?route=module/popular
When the above URL is requested , Opencart would try to find a controller file called “popular” in module folder having class name “ControllerModulePopular”. So lets extend the parent controller class.
class ControllerModulePopular extends Controller {
Whenever the controller is called, by default the index() function is executed.
$this->load->language('module/popular');

The above line of code will loads the language file variables of popular.php which is at admin/language/english/module/popular.php

Now we can get the messages or text with reference to variable like $this->language->get(‘heading_title’). So the popular text will be transferred to the template view files. The below line of code will set the document title as Popular.
$this->document->setTitle($this->language->get('heading_title'));
$this->load->model('extension/module');

All of the OpenCart modules and general settings are saved using this module. When the form is submitted through POST method and the validate function of the controller returns true, all the changes are saved and success message is assigned to success variable and will redirected to the module listing page.

if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
        if (!isset($this->request->get['module_id'])) {
            $this->model_extension_module->addModule('popular', $this->request->post);
        } else {
            $this->model_extension_module->editModule($this->request->get['module_id'], $this->request->post);
        }

$this->cache->delete('product');
$this->session->data['success'] = $this->language->get('text_success');
$this->response->redirect($this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL'));
    }

As mentioned above when the form is submitted, validation function is called. It checks whether the user has permission to modify this module or not, after that other fields validations are written. For example in this module, below validations are written:
  • Module name must be between 3 and 64 characters.
  • Product image width and height must not be left blank.
    protected function validate() {
    if (!$this->user->hasPermission('modify', 'module/popular')) {
        $this->error['warning'] = $this->language->get('error_permission');
    }
    
    if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) {
        $this->error['name'] = $this->language->get('error_name');
    }
    
    if (!$this->request->post['width']) {
        $this->error['width'] = $this->language->get('error_width');
    }
    
    if (!$this->request->post['height']) {
        $this->error['height'] = $this->language->get('error_height');
    }
        return !$this->error;      
    }
    

As stated in below lines of code, language variables values are fetched from the language file and assigned to data variables so that they will be available in the template view file.

$data['heading_title'] = $this->language->get('heading_title');
$data['text_edit']     = $this->language->get('text_edit');
$data['text_enabled']  = $this->language->get('text_enabled');
$data['text_disabled'] = $this->language->get('text_disabled');

The below lines of code will check if there is any error related to permission or the fields validation, if so then it’ll assign those error messages to the related variables and the error messages will be displayed in the template view file.

if (isset($this->error['warning'])) {
    $data['error_warning'] = $this->error['warning'];
} else {
    $data['error_warning'] = '';
}

if (isset($this->error['name'])) {
    $data['error_name'] = $this->error['name'];
} else {
    $data['error_name'] = '';
}

We have defined the array of breadcrumbs. It has some elements like text, href and separator(if required). Text element has the value to be displayed in template view file , href has the link and separator has the value to be shown between the words.

    $data['breadcrumbs'] = array();
    $data['breadcrumbs'][] = array(
        'text' => $this->language->get('text_home'),
        'href' => $this->url->link('common/dashboard', 'token=' . $this->session->data['token'], 'SSL')
    );

    $data['breadcrumbs'][] = array(
        'text' => $this->language->get('text_module'),
        'href' => $this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL')
    );

Setting the action variable depending upon the presence of module_id.

if (!isset($this->request->get['module_id'])) {
        $data['action'] = $this->url->link('module/popular', 'token=' . $this->session->data['token'], 'SSL');
    } else {
        $data['action'] = $this->url->link('module/popular', 'token=' . $this->session->data['token'] . '&module_id=' . $this->request->get['module_id'], 'SSL');
    }

If the module_id is present in URL and the action is not POST then this will fetch the module information based on the module_id. The module_info will be assigned to the variables for the template view file. You can set the default value for the fields also.

if (isset($this->request->get['module_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
        $module_info = $this->model_extension_module->getModule($this->request->get['module_id']);
    }

    if (isset($this->request->post['name'])) {
        $data['name'] = $this->request->post['name'];
    } elseif (!empty($module_info)) {
        $data['name'] = $module_info['name'];
    } else {
        $data['name'] = '';
    }

    if (isset($this->request->post['limit'])) {
        $data['limit'] = $this->request->post['limit'];
    } elseif (!empty($module_info)) {
        $data['limit'] = $module_info['limit'];
    } else {
        $data['limit'] = 5;
    }

Loading the header, footer and column_left controller and then setting the output with the $data. Finally HTML will be constructed from the template/data.

$data['header'] = $this->load->controller('common/header');
$data['column_left'] = $this->load->controller('common/column_left');
$data['footer'] = $this->load->controller('common/footer');
$this->response->setOutput($this->load->view('module/popular.tpl', $data));

Complete File


<?php
class ControllerModulePopular extends Controller {
    private $error = array(); 

public function index() {
    $this->load->language('module/popular');
    $this->document->setTitle($this->language->get('heading_title'));
    $this->load->model('extension/module');

    if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
        if (!isset($this->request->get['module_id'])) {
            $this->model_extension_module->addModule('popular', $this->request->post);
        } else {
            $this->model_extension_module->editModule($this->request->get['module_id'], $this->request->post);
        }

        $this->cache->delete('product');
        $this->session->data['success'] = $this->language->get('text_success');
        $this->response->redirect($this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL'));
    }

    $data['heading_title'] = $this->language->get('heading_title');
    $data['text_edit'] = $this->language->get('text_edit');
    $data['text_enabled'] = $this->language->get('text_enabled');
    $data['text_disabled'] = $this->language->get('text_disabled');

    $data['entry_name'] = $this->language->get('entry_name');
    $data['entry_limit'] = $this->language->get('entry_limit');
    $data['entry_width'] = $this->language->get('entry_width');
    $data['entry_height'] = $this->language->get('entry_height');
    $data['entry_status'] = $this->language->get('entry_status');

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

    if (isset($this->error['warning'])) {
        $data['error_warning'] = $this->error['warning'];
    } else {
        $data['error_warning'] = '';
    }

    if (isset($this->error['name'])) {
        $data['error_name'] = $this->error['name'];
    } else {
        $data['error_name'] = '';
    }

    if (isset($this->error['width'])) {
        $data['error_width'] = $this->error['width'];
    } else {
        $data['error_width'] = '';
    }

    if (isset($this->error['height'])) {
        $data['error_height'] = $this->error['height'];
    } else {
        $data['error_height'] = '';
    }

    $data['breadcrumbs'] = array();
    $data['breadcrumbs'][] = array(
        'text' => $this->language->get('text_home'),
        'href' => $this->url->link('common/dashboard', 'token=' . $this->session->data['token'], 'SSL')
    );

    $data['breadcrumbs'][] = array(
        'text' => $this->language->get('text_module'),
        'href' => $this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL')
    );

    if (!isset($this->request->get['module_id'])) {
        $data['breadcrumbs'][] = array(
            'text' => $this->language->get('heading_title'),
            'href' => $this->url->link('module/popular', 'token=' . $this->session->data['token'], 'SSL')
        );
    } else {
        $data['breadcrumbs'][] = array(
            'text' => $this->language->get('heading_title'),
            'href' => $this->url->link('module/popular', 'token=' . $this->session->data['token'] . '&module_id=' . $this->request->get['module_id'], 'SSL')
        );          
    }

    if (!isset($this->request->get['module_id'])) {
        $data['action'] = $this->url->link('module/popular', 'token=' . $this->session->data['token'], 'SSL');
    } else {
        $data['action'] = $this->url->link('module/popular', 'token=' . $this->session->data['token'] . '&module_id=' . $this->request->get['module_id'], 'SSL');
    }

    $data['cancel'] = $this->url->link('extension/module', 'token=' . $this->session->data['token'], 'SSL');

    if (isset($this->request->get['module_id']) && ($this->request->server['REQUEST_METHOD'] != 'POST')) {
        $module_info = $this->model_extension_module->getModule($this->request->get['module_id']);
    }

    if (isset($this->request->post['name'])) {
        $data['name'] = $this->request->post['name'];
    } elseif (!empty($module_info)) {
        $data['name'] = $module_info['name'];
    } else {
        $data['name'] = '';
    }

    if (isset($this->request->post['limit'])) {
        $data['limit'] = $this->request->post['limit'];
    } elseif (!empty($module_info)) {
        $data['limit'] = $module_info['limit'];
    } else {
        $data['limit'] = 5;
    }   

    if (isset($this->request->post['width'])) {
        $data['width'] = $this->request->post['width'];
    } elseif (!empty($module_info)) {
        $data['width'] = $module_info['width'];
    } else {
        $data['width'] = 200;
    }   

    if (isset($this->request->post['height'])) {
        $data['height'] = $this->request->post['height'];
    } elseif (!empty($module_info)) {
        $data['height'] = $module_info['height'];
    } else {
        $data['height'] = 200;
    }       

    if (isset($this->request->post['status'])) {
        $data['status'] = $this->request->post['status'];
    } elseif (!empty($module_info)) {
        $data['status'] = $module_info['status'];
    } else {
        $data['status'] = '';
    }

    $data['header'] = $this->load->controller('common/header');
    $data['column_left'] = $this->load->controller('common/column_left');
    $data['footer'] = $this->load->controller('common/footer');

    $this->response->setOutput($this->load->view('module/popular.tpl', $data));
}

protected function validate() {
    if (!$this->user->hasPermission('modify', 'module/popular')) {
        $this->error['warning'] = $this->language->get('error_permission');
    }

    if ((utf8_strlen($this->request->post['name']) < 3) || (utf8_strlen($this->request->post['name']) > 64)) {
        $this->error['name'] = $this->language->get('error_name');
    }

    if (!$this->request->post['width']) {
        $this->error['width'] = $this->language->get('error_width');
    }

    if (!$this->request->post['height']) {
        $this->error['height'] = $this->language->get('error_height');
    }

    return !$this->error;
    }
  }
?>

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_module']      = 'Modules';
$_['text_success']     = 'Success: You have modified module "Popular Products"!';
$_['text_edit']        = 'Edit Popular Products Module';

// Entry
$_['entry_name']       = 'Module Name';
$_['entry_limit']      = 'Limit';
$_['entry_width']      = 'Width';
$_['entry_height']     = 'Height';
$_['entry_status']     = 'Status';

// Error
$_['error_permission']  = 'Warning: You do not have permission to modify module "Popular Products"!';
$_['error_width']       = 'Width required!';
$_['error_height']      = 'Height required!';

View File

View file refers to the template file having .tpl extension. It will display all the set variables of the controller.

Then after we loop through the $breadcrumbs array to display breadcrumbs links.One section is added to display the error messages.We have one form and when its submitted, controller method is called and submitted data will be processed.

<?php echo $header; ?><?php echo $column_left; ?>
<div id="content">
  <div class="page-header">
    <div class="container-fluid">
      <div class="pull-right">
        <button type="submit" form="form-popular" data-toggle="tooltip" title="<?php echo $button_save; ?>" class="btn btn-primary"><i class="fa fa-save"></i></button>
        <a href="<?php echo $cancel; ?>" data-toggle="tooltip" title="<?php echo $button_cancel; ?>" class="btn btn-default"><i class="fa fa-reply"></i></a></div>
      <h1><?php echo $heading_title; ?></h1>
      <ul class="breadcrumb">
        <?php foreach ($breadcrumbs as $breadcrumb) { ?>
        <li><a href="<?php echo $breadcrumb['href']; ?>"><?php echo $breadcrumb['text']; ?></a></li>
        <?php } ?>
      </ul>
    </div>
  </div>
  <div class="container-fluid">
    <?php if ($error_warning) { ?>
    <div class="alert alert-danger"><i class="fa fa-exclamation-circle"></i> <?php echo $error_warning; ?>
      <button type="button" class="close" data-dismiss="alert">&times;</button>
    </div>
    <?php } ?>
    <div class="panel panel-default">
      <div class="panel-heading">
        <h3 class="panel-title"><i class="fa fa-pencil"></i> <?php echo $text_edit; ?></h3>
      </div>
      <div class="panel-body">
        <form action="<?php echo $action; ?>" method="post" enctype="multipart/form-data" id="form-popular" class="form-horizontal">
          <div class="form-group">
            <label class="col-sm-2 control-label" for="input-name"><?php echo $entry_name; ?></label>
            <div class="col-sm-10">
              <input type="text" name="name" value="<?php echo $name; ?>" placeholder="<?php echo $entry_name; ?>" id="input-name" class="form-control" />
              <?php if ($error_name) { ?>
              <div class="text-danger"><?php echo $error_name; ?></div>
              <?php } ?>
            </div>
          </div>
          <div class="form-group">
            <label class="col-sm-2 control-label" for="input-limit"><?php echo $entry_limit; ?></label>
            <div class="col-sm-10">
              <input type="text" name="limit" value="<?php echo $limit; ?>" placeholder="<?php echo $entry_limit; ?>" id="input-limit" class="form-control" />
            </div>
          </div>
          <div class="form-group">
            <label class="col-sm-2 control-label" for="input-width"><?php echo $entry_width; ?></label>
            <div class="col-sm-10">
              <input type="text" name="width" value="<?php echo $width; ?>" placeholder="<?php echo $entry_width; ?>" id="input-width" class="form-control" />
              <?php if ($error_width) { ?>
              <div class="text-danger"><?php echo $error_width; ?></div>
              <?php } ?>
            </div>
          </div>
          <div class="form-group">
            <label class="col-sm-2 control-label" for="input-height"><?php echo $entry_height; ?></label>
            <div class="col-sm-10">
              <input type="text" name="height" value="<?php echo $height; ?>" placeholder="<?php echo $entry_height; ?>" id="input-height" class="form-control" />
              <?php if ($error_height) { ?>
              <div class="text-danger"><?php echo $error_height; ?></div>
              <?php } ?>
            </div>
          </div>
          <div class="form-group">
            <label class="col-sm-2 control-label" for="input-status"><?php echo $entry_status; ?></label>
            <div class="col-sm-10">
              <select name="status" id="input-status" class="form-control">
                <?php if ($status) { ?>
                <option value="1" selected="selected"><?php echo $text_enabled; ?></option>
                <option value="0"><?php echo $text_disabled; ?></option>
                <?php } else { ?>
                <option value="1"><?php echo $text_enabled; ?></option>
                <option value="0" selected="selected"><?php echo $text_disabled; ?></option>
                <?php } ?>
              </select>
            </div>
          </div>
        </form>
      </div>
    </div>
  </div>
</div>
<?php echo $footer; ?>

Conclusion


In this part of article, we tried to develop a popular products OpenCart module using MVC. It’s easy to modify the OpenCart modules if you know the basic core concepts of MVC.

Read Next Part of this article to know How to Display the Popular Products in the opencart store frontend. 

Ketan Patel

About Ketan Patel -

I have developed a wide range of websites using CorePHP, Opencart, CakePHP and CodeIgniter including sites for startup companies and small businesses. Apart from my blogging life, I like to read Novels, Listening music and Net surfing.

Subscribe to this Blog via Email :