User Tools

Site Tools

Back Office Menu Entries

The new Tab in back office.

The new Tab in back office.

Best practice for modules adding services in back office is to not offer these services on their configuration page, but to add an entry to one of the back office menus. This strategy may well become mandatory one day.

Going this route isn't particularly difficult. One big advantage: one doesn't have to go through getContent() in the module main class and build up everything from there. One can use 'real' controllers, just like all the controllers and classes coming with thirty bees core.

Code snippets here were written while starting with the Git Updater module. You can find complete files with similar code (names adjusted for that module) in the early commits there.

Install the Tab / Menu Item

Note: while entries in a menu are usually called menu item or similar, thirty bees source code calls them Tabs. Base class Tabs, controller AdminTabsController, and so on.

Define a Controller

As written above, Tabs run a controller. First step to define the name of this controller. Using a class constant is a good idea.

class MyModule extends Module
{
    const MAIN_CONTROLLER = 'AdminMy';
 
    // ...
}

Note that the controller name is defined to just AdminMy, not AdminMyController. The Controller part gets added by thirty bees core automatically. Prepending admin controller names with Admin is the usual convention.

Installing the Tab

Installation code obviously goes into install() of the module main class. Here is how it can be done:

public function install()
{
    $success = parent::install();
 
    if ($success) {
        try {
            $tab = new Tab();
 
            $tab->module      = $this->name;
            $tab->class_name  = static::MAIN_CONTROLLER;
            $tab->id_parent   = Tab::getIdFromClassName('AdminPriceRule');
 
            # As of thirty bees v1.0.8, having a name is no longer mandatory.
            $langs = Language::getLanguages();
            foreach ($langs as $lang) {
                $tab->name[$lang['id_lang']] = $this->l('Updater');
            }
 
            $success = $tab->save();
        } catch (Exception $e) {
            $success = false;
        }
    }
 
    return $success;
}
$tab->module

That's the module which created the Tab. Always $this->name.

$tab->class_name

This defines the controller class for the Tab. Without the trailing Controller, see previous section.

$tab->id_parent

Tab ID of the parent Tab. If NULL, the new Tab gets appended to the menu top level. Else it gets appended to the submenu of this parent.

There are multiple ways to find the parent ID. One is to look up the HTML ID of the existing menu entry and stripping maintab- from it. Hardcoding this ID isn't a good idea, it might change in the future.

For the purpose of this HowTo, AdminPriceRule was choosen, because it has the shortest submenu, giving the most compact screenshots.

$tab->name

This is the user visible name of the Tab. As thirty bees supports multiple languages, one has to set each language.

$tab->save()

This saves the Tab into the database. Should be repeated after each change of the Tab. And yes, Tabs defined by modules are visible everywhere, not just from code inside the defining module.

Uninstalling the Tab

Being nice code writers, we uninstall Tabs on module uninstallation, of course. Nobody wants to be responsible for useless clutter in the database.

Nice enough, that's pretty simple:

public function uninstall()
{
    $success = true;
 
    $tabs = Tab::getCollectionFromModule($this->name);
    foreach ($tabs as $tab) {
        $success = $success && $tab->delete();
    }
 
    return $success && parent::uninstall();
}

This uninstalls all module related Tabs, in case the module installed more than one.

Enabling and Disabling

One might wonder what happens with module defined Tabs when the module gets disabled, but not uninstalled.

Nice feature of such Tabs is, they automatically get disabled together with disabling the module. No need for the module developer to take care of this. A disabled Tab is still recorded in the database, nevertheless it disappears from the back office menu.

Results

Lo and behold, with the above code additions and installing (or resetting) the module, a submenu item to menu item Price Rules appears in back office:

New Tab in back office.

New Tab in back office.

One can disable or uninstall the module, the menu item disappears. Re-enabling the module gets the menu item back. This is how it should work. Using a database browser, one can also see see a record appearing in table tb_tabs, and one record for each language in table tb_tabs_lang. These records stay with the module disabled and disappear on module uninstallation.

However, when clicking this menu item, one sees this:

(picture)

No heading for the page, no controller found. This will get corrected in the next section.

Creating a Controller

This section shows how to get a bare minimum controller into work. Just to show how the mechanism of accepting controllers works.

A Bare Minimum Controller

As usual in this wiki, with all the documentation comments stripped:

AdminMyController.php
<?php
if (!defined('_TB_VERSION_')) {
    exit;
}
 
class AdminMyController extends ModuleAdminController
{
    public function __construct()
    {
        $this->bootstrap = true;
 
        parent::__construct();
    }
 
    public function initContent()
    {
        $this->page_header_toolbar_title = $this->l('My Controller');
 
        parent::initContent();
    }
}
Class Name

Base name of the class should match class_name of the Tab. Which got defined on Tab creation, in install() in the module main class. Additionally, a Controller gets appended.

Parent Class

Subclassing ModuleAdminController instead of AdminController. Former is a thin wrapper around the latter, loading the module as needed and bending template search paths into the module directory.

$this->bootstrap

This is a flag to declare compatibility with Bootstrap, the CSS layout system. Should always be set to true.

$this->page_header_toolbar_title

This is the title of the controller page. Translated as needed, of course. See also Adding a Title (page Back Office Display Decoration).

Controller Code File Placement

thirty bees' dispatcher prefers certain locations where code files should be placed. Following these wishes, controllers get loaded without additional efforts.

Search strategy for back office controllers can be found in Dispatcher->dispatch(), case static::FC_ADMIN. For controllers inside modules it looks like this:

  • All back office controllers go into controllers/admin/ inside the module directory.
  • Name of the PHP file should match the name of the class inside. Plus the .php suffix, of course.
  • File names without Controller appended get accepted as well.
  • Only for retrocompatibility, code files in the module root get found as well. Don't use this.

Accordingly, the file shown above goes into controllers/admin/AdminMyController.php.

After placing the file there, delete cache/class_index.php. It'll get rebuilt on the next page request. Then including the new controller.

Results

Back office controller found!

Back office controller found!

Looks promising, doesn't it?

Actual Content Display

Strategies to display actual content with the controller went into dedicated pages. For a list, see section Back Office Display (page Writing Controllers).

back_office_menu_entries.txt ยท Last modified: 2018/11/30 19:04 by Traumflug