Writing your own Prestashop Module - Part 3

Storing Module Configuration

Introduction

In the third part of this series we'll look at how we can store configuration data for our modules in the Prestashop database, and how we can allow users to interact with this data to control the module's behaviour. We will also briefly touch on how we can generate output from our module to provide visual feedback of the configuration changes.

Updated for Prestashop version 1.4 onwards.

Managing user settings

We're going to call this module "TutorialSecond" so we again need to create a new module directory and class file -- which will be named "tutorialsecond" and "tutorialsecond.php" respectively. The class file (tutorialsecond.php) should contain the following:

class Tutorialsecond extends Module
{
  private $_html = '';

  function __construct()
  {
    $version_mask = explode('.', _PS_VERSION_, 2);
    $version_test = $version_mask[0] > 0 && $version_mask[1] > 3;
    $this->name = 'tutorialsecond';
    $this->tab = $version_test ? 'others' : 'eCartService.net Tutorials';
    if ($version_test)
      $this->author = 'eCartService.net';
    $this->version = '0.1.0';
    parent::__construct();
    $this->displayName = $this->l('Second Tutorial Module');
    $this->description = $this->l('Our second module - A "Hello world" redux.');
  }

  public function getContent()
  {
  }

  private function _displayForm()
  {
  }
}
// End of: tutorialsecond.php

You will notice that we have implemented two new member functions ::getContent() and ::_displayForm(). If you upload the file to your server and install this module at this stage you should see a new option on the modules list screen for 'Second Tutorial Module'. There will now be a ">> Configure" link in the module entry, although clicking it will merely return an empty Back Office page. The presence of the ::getContent() member function is responsible for this as it provides the interface between our module and the Back Office.

In addition to these two new functions we have also added the $_html private member variable, which we will use later in this article to build the required output for display in the Back Office.

Storing and Retrieving Configuration Data

Prestashop provides a "Configuration" class which offers several member functions to manipulate configuration data, but the two key functions that will be most commonly used are:

Configuration::updateValue($key, $values, $html = false);
Configuration::get($key, $id_lang = NULL);

The Configuration::updateValue() function allows us to store a configuration option in the database (optionally in multiple languages in which case the $values parameter will be an array) and the Configuration::get() function allows us to retrieve configuration data for a selected or store default language. We will ignore the parameters that have default values for now, but will revisit the $html parameter in the Configuration::updateValue() function in the next article when we look at the subject of form validation.

Implementing the Configure Screen

In our source file we created a private member function _displayForm(). This is entirely optional as all of the interface code could be placed in the getContent() member function, however it is highly recommended that you separate this out for the sake of easier code maintenance. We'll create a simple form within this function from with which we can capture store owner input.

private function _displayForm()
{
  $this->_html .= '
    <form action="'.$_SERVER['REQUEST_URI'].'" method="post">
      <label>'.$this->l('Message to the world').'</label>
      <div class="margin-form">
        <input type="text" name="our_message" />
      </div>
      <input type="submit" name="submit" value="'.$this->l('Update').'" class="button" />
    </form>';
}

You can see that the ::displayForm() function simply appends standard html form code to our $html member variable, with the form target being $SERVER['REQUESTURI']. Prestashop's Back Office architecture will route this to our ::getContent class member function for us to handle when the form is posted.

We next need to add code to our ::getContent() function to actually display the form and handle the form submission.

public function getContent()
{
  if (Tools::isSubmit('submit'))
  {
    Configuration::updateValue($this->name.'_message', Tools::getValue('our_message'));
  }
  $this->_displayForm();
  return $this->_html;
}

The ::getContent() function first uses another Prestashop class "Tools" to test whether we are handling the post action from the form, or whether this function is being called in another way i.e. a click on the "Configure" link in the module list -- the parameter is the name we gave to our "update" button in the form.

If the function is being called directly by the Back Office (in which case Tools::isSubmit('submit') will return false), then we call the form rendering function we created above and return the output, captured in the $this->_html variable, to the Back Office for display.

If the function is being called as a result of our form being posted, then we can store our configuration parameter in the database with the value entered on our form. We again use the Tools class to obtain the value of the form variable using the call to Tools::getValue('ourmessage') where 'ourmessage' is the name of the input field in our form.

You can see that I have added the name of our module to the beginning of the configuration item name -- this is to ensure that the configuration key is unique as the namespace for these keys is shared for the entire store.

Once we have stored our configuration data the form is again displayed, ready for more input if required.

Module Output on a Page

We now have a method of capturing input from the store owner and saving it in the database, so the next obvious step would be to do something with it e.g. display it on a page. In part 1 of the series we talked about "hooks" in relation to modules adding functionality. In order to utilise this we need to tell PrestaShop that our module would like to hook into the Front office and we do this using the following function (defined in the Module base class):

$this->registerHook($hook_name);

The $hook_name parameter refers to one of the different points that the Prestashop core allows modules to insert output and/or data processing, but for now we will use one in particular -- "leftColumn', which allows us to add content in the left column of all pages. In order to initialise the hook we need to add the following new override in our own class:

public function install()
{
  parent::install();
  if (!$this->registerHook('leftColumn'))
    return false;
}

This tells Prestashop to execute our module hook when it is rendering the content for the left column of all pages. We next need to add a function to handle the hook callback. The convention is to name the function as the Hook name preceded by "hook":

public function hookLeftColumn()
{
  return '<div class="block&quot"<h4>'. Configuration::get($this->name.'_message') . '</h4></div>';
}

In our simple example we are just wrapping our configuration parameter in some standard html markup so that it is displayed correctly on our page.

If you have already installed the module you should now uninstall then reinstall to ensure that the hook is correctly registered with the Prestashop core. Now you can enter a value in the configuration screen for the module -- e.g. 'Hello World', and it will be output as the heading of a box in the left column of your store. The full code for the second example should now look like:

class Tutorialsecond extends Module
{
  private $_html = '';

  function __construct()
  {
    $version_mask = explode('.', _PS_VERSION_, 2);
    $version_test = $version_mask[0] > 0 && $version_mask[1] > 3;
    $this->name = 'tutorialsecond';
    $this->tab = $version_test ? 'others' : 'eCartService.net Tutorials';
    if ($version_test)
      $this->author = 'eCartService.net';
    $this->version = '0.1.0';

    parent::__construct();
    $this->displayName = $this->l('Second Tutorial Module');
    $this->description = $this->l('Our second module - A "Hello world" redux.');
  }

  public function install()
  {
    parent::install();
    if (!$this->registerHook('leftColumn'))
      return false;
  }

  public function getContent()
  {
    if (Tools::isSubmit('submit'))
    {
      Configuration::updateValue($this->name.'_message', Tools::getValue('our_message'));
    }
    $this->_displayForm();
    return $this->_html;
  }

  private function _displayForm()
  {
    $this->_html .= '
      <form action="'.$_SERVER['REQUEST_URI'].'" method="post">
        <label>'.$this->l('Message to the world').'</label>
        <div class="margin-form">
          <input type="text" name="our_message" />
        </div>
        <input type="submit" name="submit" value="'.$this->l('Update').'" class="button" />
      </form>';
  }

  public function hookLeftColumn()
  {
    return '<div class="block"><h4>'. Configuration::get($this->name.'_message') . '</h4></div>';
  }
}
// End of: tutorialsecond.php

Summary

In this article we have extended our first module to include a basic configuration form and have used a hook function to display output from our module in the left column of our pages. In the next part of this series we will look at improving the configuration facility into something that could be used in a real module implementation. This will include validating user input to the form, pre-populating the form fields as appropriate based on the current configuration and displaying module errors and warnings in the Back Office screens.