User Tools

Site Tools

Using Helper For Options

This page expects knowledge of the basic Back Office Display Engine. Also note page Back Office Display Decoration, which describes the widgets not in, but around main content.

In quite a number of cases, back office controllers do other things than dealing with data stored in a dedicated table in the database. For example, many modules store just a few configuration options. Other controllers deal with files on disk, updates, external APIs or such stuff. The Helper Engine, being brilliant, provides a simple interface for such cases as well.

Usage for Database Storage

Let's start with a simple, yet already useful example.

Step 1

Create an admin controller. One way, including code to actually execute it, is described in Back Office Menu Entries.

Step 2

Make the controller constructor look like this:

public function __construct()
{
    $this->bootstrap = true;
 
    $this->fields_options = [
        'firstpanel' => [
            'submit'      => [
                'title'     => $this->l('Save'),
            ],
            'fields' => [
                'MYCONTROLLER_OPTION' => [
                    'type'          => 'text',
                ],
            ],
        ],
    ];
 
    parent::__construct();
}
Result

The controller panel should work already. To test this, navigate to the controller page, enter a value in the text field and click on Save.

For verification, look up table tb_configuration with a database browser, e.g. phpMySQL, and do this query:

SELECT `id_configuration`, `name`, `value`
FROM `tb_configuration`
WHERE `name` = 'MYCONTROLLER_OPTION'

Query result follows changed values in back office? Congratulations!

Usage for Additional Tasks

One way to do additional tasks is to implement a field handling method.

Step 1

Implement a controller like in the previous example.

Step 2

Additionally, put this field handler method into the controller:

public function updateOptionMycontrollerOption($value)
{
    // some code
}

Name of the method is crucial here. It's the name of the option (MYCONTROLLER_OPTION), converted to CamelCase ( -> MycontrollerOption), appended to updateOption.

Result

Having this additional method, two things happen:

  • Database is left unchanged regarding this field.
  • Instead, the field handling method is invoked, with field content as parameter.

If there are additional fields without handling method, these still get updated in the database. One can also implement multiple handlers, one for each field.

Field handlers and database updates get invoked in the order they're defined in the panel definition. If a handler wants to deal with data from other fields, its field should be defined after them. Note that there's a Helper Field 'hidden', which allows to just handle something without changing the user interface.

Usage for Database Independent Tasks

If one wants to do entirely different tasks on submit, one simply implements initProcess() in the controller. Like this:

public function initProcess()
{
    // some code
 
    // optional
    parent::initProcess();
 
    // some code
}

Calling the parent is optional and invokes default behavior. No return values from there.

Notably, this method gets called for every submit action, so the first task should be to test which type of submit actually happened. Also wether the submitting user has sufficient privileges for the operation. For ideas on how to do this, a look into AdminController->initProcess() is helpful.

In this method, submitted data can be found in $_POST. An example:

[
  'MYCONTROLLER_OPTION' => 'something else',
  'MYCONTROLLER_OPTION2' => '',
  'submitOptionsconfiguration' => '',
]

In this example, first two elements are key-value pairs of the submitted fields. Third element is the name of the HTML submit button used, which defaults to submitOptions, appended by $this->table of the controller.

Adding Panels

This section is about adding panels at all. On options and features for these panels, see next section.

Helper generated set of panels.

Helper generated set of panels.

Code used to generate this screenshot is the code below.

PHP Code

The following code usually goes into the constructor. This way, database edits without further code just work. Actual usage of this defined array happens in renderOptions() and processUpdateOptions(), in case one wants to look up implementation details or replace default handling.

public function __construct()
{
    $this->bootstrap = true;
 
    $this->fields_options = [
        'firstpanel' => [
            'title'        => $this->l('My Panel'),
            'fields' => [
                'MYCONTROLLER_OPTION_1' => [
                    'type'          => 'text',
                ],
                'MYCONTROLLER_OPTION_2' => [
                    'type'          => 'text',
                ],
            ],
        ],
        'secondpanel' => [
            'title'       => $this->l('My Other Panel'),
            'fields' => [
                'MYCONTROLLER_OPTION_3' => [
                    'type'          => 'text',
                ],
            ],
        ],
    ];
 
    parent::__construct();
}

How it Works

To render panels using the Options Helper, all one has to do is to set $this->bootstrap to true and to put a well formed array structure into $this->fields_options.

First Level

First level defines the panels them selfs, one entry per panel.

Keys get used to generate an element ID in HTML output, making it reachable for CSS and JavaScript. The above key 'firstpanel' results in an ID 'configuration_fieldset_firstpanel', where 'configuration' is $this->table of the calling controller, which happens to default to 'configuration'.

Second Level

Second level are panel properties, one set in each first level entry. More on them in section Panel Properties.

Third Level

Third level are entries inside 'fields' of the second level.

Keys of these entries play a notable role. In the standard case, using option panels to edit key-value pairs in database table configuration, this key is taken as the key for the database entry. Value of the pair is the content of the related HTML input field.

As most options needed by modules usually get stored in the same database table configuration, it's highly recommended to give these options a name which starts with the controller name. This should avoid name collisions between modules. Using all uppercase names with underscores is the usual convention.

Each of these third level entries is a Helper field.

HTML Output

Ignoring whitespace, one gets this HTML:

<form action="index.php?controller=MyController&amp;token=(login token)"
      id="configuration_form"
      method="post"
      enctype="multipart/form-data"
      class="form-horizontal">
  <div class="panel " id="configuration_fieldset_firstpanel">
    <div class="panel-heading">
      <i class="icon-cogs"></i>
      My Panel
    </div>
    <div class="form-wrapper">
      <div class="form-group">
        <div id="conf_id_MYCONTROLLER_OPTION_1">
          <div class="col-lg-9">
            <input class="form-control "
                   type="text"
                   size="5"
                   name="MYCONTROLLER_OPTION_1"
                   value=""
                   />
          </div>
        </div>
      </div>
      <div class="form-group">
        <div id="conf_id_MYCONTROLLER_OPTION_2">
          <div class="col-lg-9">
            <input class="form-control "
                   type="text"
                   size="5"
                   name="MYCONTROLLER_OPTION_2"
                   value=""/>
          </div>
        </div>
      </div>
    </div><!-- /.form-wrapper -->
  </div>
  <div class="panel " id="configuration_fieldset_secondpanel">
    <div class="panel-heading">
      <i class="icon-cogs"></i>
      My Other Panel
    </div>
    <div class="form-wrapper">
      <div class="form-group">
        <div id="conf_id_MYCONTROLLER_OPTION_2">
          <div class="col-lg-9">
            <input class="form-control "
                   type="text"
                   size="5"
                   name="MYCONTROLLER_OPTION_2"
                   value=""/>
          </div>
        </div>
      </div>
    </div><!-- /.form-wrapper -->
  </div>
</form>

Notably, key elements in this HTML get an ID built from Helper parameters, to allow accessing them by JavaScript.

Panel Properties

This section is about properties of individual panels.

Helper generated panel.

Helper generated panel.

Code used to generate this screenshot is the code below.

PHP Code

This section takes a closer look at what happens inside a panel. It also looks at the submit button and other buttons.

Having a submit button defined, updating entered fields in the database works automagically, unless one bends too many parameters away from the defaults.

One caveat: while rendering key-value pairs from other tables than the default table configure is implemented in renderOptions(), actually updating these values automagically, which should happen in processUpdateOptions(), is currently not. Apparently nobody found a need for such a feature so far. Automagical updates work for table configure, only.

'firstpanel' => [
    'title'       => $this->l('My Panel'),
    'top'         => $this->l('Text to display above the panel.'),
    'icon'        => 'icon-coffee',
    'description' => $this->l('Display as description'),
    'info'        => $this->l('Display as panel info'),
    'bottom'      => $this->l('Text to display below the panel.'),
    'class'       => 'myclass',
    'fields' => [
        [...]
    ],
    'submit'      => [
        'title'     => $this->l('Save'),
        'imgclass'  => 'anchor',
        'type'      => 'submit',
        'name'      => 'buttonname',
        'id'        => 'buttonid',
    ],
    'buttons'     => [
        [
            'href'  => '#',
            'title' => $this->l('Extra Button'),
            'icon'  => 'icon-cog',
            'id'    => 'myid',
            'class' => 'myclass',
            'js'    => 'clickMe();',
        ],
    ],
],

Parameter Description

'title'

Heading of the panel. HTML allowed. Defaults to Options.

'top'

Text to be displayed above the panel. HTML allowed. Optional.

'icon'

Name of the icon to show in front of the Heading. That's currently FontAwesome 3 icons. Defaults to icon-cogs.

'description'

Text to be displayed at the top of the panel in a (blue) info box. HTML allowed. Optional.

'info'

Text to be displayed at the top of the panel without an info box. Not to be confused with the title of the first field. Plain text, no HTML. Optional.

'bottom'

Text to be displayed below the panel. HTML allowed. Optional.

'class'

CSS class(es) for the panel's DIV. Optional.

'fields'

List of Helper Fields.

'submit' and 'buttons'

With either 'submit' or 'buttons' defined, the panel gets a footer with buttons inside.

Each of these two properties is an array with sub-properties. In case of 'buttons' even an array of such arrays, to allow defining multiple buttons.

'submit':'title'

Text to display on the button. HTML allowed. Mandatory.

'submit':'imgclass'

Icon for the HTML button. The given name gets prepended with process-icon-, resulting in a CSS class to be found in admin-dev/themes/default/sass/partials/_icons.sass. Defaults to save, which displays process-icon-save.

'submit':'type'

Type of the HTML button. Defaults to submit.

'submit':'name'

Name of the HTML button. This is the name of the action PHP sees on submit. Defaults to submitOptions, appended by $this->table of the controller.

Note: providing a name not matching one of the well known names (submitReset, submitFilter, submitOptions,…, see AdminController->initProcess() and AdminController->postProcess()), bypasses automatic actions like storing values in the database. This is an advantage if the module isn't about storing parameters in the database. In this case, incoming data should be handled in the constructor or in postProcess() of the controller.

'submit':'id'

HTML ID of the HTML button. Optional.

'buttons'::'type'

Type of the HTML button. Defaults to button. Ignored if 'href' is defined.

'buttons'::'href'

Defining this property displays not a HTML button, but an HTML anchor, looking like a button, instead. Value of this property is the HREF for this anchor.

'buttons'::'title'

Text to display on the button. HTML allowed. Mandatory.

'buttons'::'icon'

Icon to display on the button. Same strategy as the icon for the panel title, see above. Optional.

'buttons'::'id'

HTML ID for the button. Optional.

'buttons'::'class'

CSS class(es) for the button. In addition to classes btn and btn-default on HREF-type buttons, instead of these on the others.

'buttons'::'js'

JavaScript code to execute on a button click.

HTML ID of the Panel

There is no explicit property for the HTML ID. It gets calculated with {$table}_fieldset_{$category}, where $table is $this->table of the controller and $category the key of the properties array ('firstpanel' in the example).

Common to All

Note that $this->l(), the translation method, removes HTML directives. If you need HTML as well as a translation, do it like this:

'<b>'.$this->l('translatable text').'</b>'

HTML Output

Ignoring whitespace, one gets this HTML:

<form action="index.php?controller=MyController&amp;token=(login token)"
      id="configuration_form"
      method="post"
      enctype="multipart/form-data"
      class="form-horizontal">
  Text to display above the panel.
  <div class="panel myclass" id="configuration_fieldset_firstpanel">
    <div class="panel-heading">
      <i class="icon-coffee"></i>
      My Panel
    </div>
    <div class="alert alert-info">Display as description</div>
    <div>Display as panel info</div>
    <div class="form-wrapper">
      <!-- ... set of fields ... -->
    </div><!-- /.form-wrapper -->
    Text to display below the panel.
    <div class="panel-footer">
      <button type="submit"
              id="buttonid"
              class="btn btn-default pull-right"
              name="buttonname">
        <i class="process-icon-anchor"></i>
        Save
      </button>
      <a href="#"
         id="myid"
         class="btn btn-default myclass"
         onclick="clickMe();">
        <i class="icon-cog" ></i>
        Extra Button
      </a>
    </div>
  </div>
</form>

Panel with Multiple Tabs

TODO: Helper can also separate fields into multiple tabs. For an example, see AdminThemesController. Key to success is obviously an array 'tabs' in the top level, then an entry 'tab' in each field.

AdminController->renderOptions() calls hook 'action'.$this->controller_name.'OptionsModifier' before rendering. This hooks allows to edit $this->fields_options and add template variables to $this->tpl_option_vars.

Making Variables Available for JavaScript

In some cases it's necessary to forward variables from PHP to JavaScript. While the Helper engine has no direct support for this, there are options available.

Using One Hidden Field Per Variable

If only one, or only few variables need forwarding, using a hidden field is a good choice.

PHP Code

Snippet to insert as one of the Helper fields:

'MY_CONTROLLER_VARIABLE' => [
    'type'        => 'hidden',
    'auto_value'  => false,
    'value'       => _TB_VERSION_,
],

JavaScript Code

Example to use the value in JavaScript:

console.log($('input[name=MY_CONTROLLER_VARIABLE]').val());

Caveats

TODO: describe how to avoid storing the value in the database.

Using One Hidden Field for Many Variables

With JSON encoding, the same technique can be used for many variables:

PHP Code

Snippet to insert as one of the Helper fields:

'MY_CONTROLLER_VARIABLES' => [
    'type'        => 'hidden',
    'auto_value'  => false,
    'value'       => htmlspecialchars(json_encode([
        'currentVersion'    => _TB_VERSION_,
        'repoApiUrl'        => 'https://api.thirtybees.com/',
        'availableChannels' => [ 'channel1', 'channel2' ],
    ])),
],

JavaScript Code

Example to use the value in JavaScript:

let json = JSON.parse($('input[name=MY_CONTROLLER_VARIABLES]').val());
console.log(json.currentVersion);
console.log(json.repoApiUrl);
console.log(json.availableChannels);

Avoid Database Storage

Especially when using fields just for passing parameters, one probably doesn't want them to be stored in the database. That'd be just a waste of resources.

PHP Code

The simple trick is to put an empty field handler method into the controller:

public function updateOptionMycontrollerOption() { }

That's all. For finding the required method name, see above.

Sophisticated Usage

(Further tips and tricks should be added here)

References

  • File admin-dev/themes/default/template/helpers/options/options.tpl
  • generateOptions() in classes/helper/HelperOptions.php
  • renderOptions() and processUpdateOptions() in classes/controller/AdminController.php
using_helper_for_options.txt · Last modified: 2019/01/28 18:48 by Traumflug