Templates and Keys

Template Architecture

The Melbis platform uses MVC architecture: data processing logic is concentrated in the module’s PHP code, while HTML generation is handled by templates. Although PHP allows you to assemble all HTML directly in the module using simple string substitution operations, we strongly recommend against doing so. The Melbis template engine is equipped with powerful tools: loops, conditions, modifiers, callbacks, nesting — all of this frees the module from display logic and makes the code cleaner and more maintainable.

Template File Structure

Templates are stored in the templates/ directory at the project root. Inside are named template groups:

templates/
    default/
        units/
            melbis_base_page/
                main.htm
                page_index.htm
                page_goods.htm
                page_404.htm
            melbis_store_card/
                main.htm
            melbis_cataloge/
                main.htm

Each module has its own subdirectory inside units/, which stores all its .htm templates. A single module can contain any number of templates — they are loaded by Tpl* methods using the filename without the extension.

Template Groups

A template group is a named set of all .htm files in the project. The default group is used by default. If desired, you can create additional groups — for example, for a mobile version of the site or an alternative design. Once a new group is created, it will automatically appear in each module’s tree in the IDE.

The active group is set in config.json (the MELBIS_TEMPLATE parameter) or through the “Design → Installation” dialog — this is described in detail in the “Configuration” section.

If necessary, the group can be switched directly in the root script before calling Run:

MELBIS()->TemplateSet('mobile');
MELBIS()->Run($entry_point, $entry_param);

This is convenient, for example, for automatically selecting a mobile template based on the User-Agent.

Template Methods

All module interactions with the template engine occur through MELBIS()->Tpl* methods:

TplCreate() — creates a new template engine context and returns its pointer. Called at the beginning of each module:

$tpl = MELBIS()->TplCreate();

TplAssign($tpl, $vars) — passes data to the context. Accepts both an array (all keys become template variables) and a “key name” + “value” pair:

// Pass the entire record array
MELBIS()->TplAssign($tpl, $product);

// Pass a single value
MELBIS()->TplAssign($tpl, 'TITLE', 'Product Catalog');

// Pass a nested array (accessible in the template as {PAGE:TITLE}, etc.)
MELBIS()->TplAssign($tpl, 'PAGE', $page);

TplParse($tpl, $var, $file) — loads an .htm template by filename, parses it with the current context data, and stores the result in a variable. This is the method the module uses to assemble the various parts of its HTML:

// Parse the page_index.htm template and write to the CONTENT variable
MELBIS()->TplParse($tpl, 'CONTENT', 'page_index');

// Additional templates
MELBIS()->TplParse($tpl, 'WINDOWS', 'windows');
MELBIS()->TplParse($tpl, 'SCRIPTS', 'scripts');

TplFinal($tpl, $file) — the final step: parses the specified template (typically main) with the current context state (including variables already populated by TplParse) and returns the finished HTML string. The result is returned from the module’s main function:

return MELBIS()->TplFinal($tpl, 'main');

TplAppend($tpl, $var, $value) — appends a value to an existing context variable without overwriting it. Useful for accumulating HTML in parts.

Thus, a typical module workflow with the template engine looks like this:

function MELBIS_BASE_PAGE($mVars)
{
    $tpl = MELBIS()->TplCreate();

    // ... retrieve data from the database ...

    // Pass data
    MELBIS()->TplAssign($tpl, 'PAGE', $page);
    MELBIS()->TplAssign($tpl, 'GOODS', $goods);

    // Assemble the required content variant
    MELBIS()->TplParse($tpl, 'CONTENT', 'page_goods');

    // Assemble additional parts
    MELBIS()->TplParse($tpl, 'SCRIPTS', 'scripts');

    // Return the final HTML
    return MELBIS()->TplFinal($tpl, 'main');
}

The template engine operates on a safe pipeline principle: it silently ignores missing keys (turning null into an empty string), blocks direct array output to avoid PHP errors, and allows building modifier chains.

1. Variable Output and Data Handling

The Melbis Shop template engine can work not only with flat variables, but also with deeply nested arrays, and supports data exchange between different system modules.

1.1. Basic Syntax

1.2. Accessing Nested Arrays (Multidimensional Data)

If a module passes not just a string to the template, but a complex multidimensional array (for example, an array with profile settings or prices), you can access any level of nesting using a colon :.

The template engine automatically “flattens” arrays, creating convenient keys. Syntax: {ARRAY:KEY:SUBKEY}

Usage examples:

1.3. Working with Simple (Flat) Lists

Modules often pass not complex associative arrays, but simple value lists (for example, an array of tags, a list of IDs, a set of colors: ['red', 'blue', 'green']).

To output such lists in a loop, the parser automatically creates a virtual key ITEM, which holds the current value.

How it works: Inside the loop {#LIST} ... {LIST#} you simply use the {ITEM} tag. Since this is now a full engine element, all modifiers and system variables can be applied to it.

Example (Outputting tags separated by commas, without a trailing comma):

Passed array: $mData['TAGS'] = ['php', 'mysql', 'js'];

In the template:
{#TAGS}
    <a href="/search/?q=[ITEM]">{ITEM|html}</a>{*!IS_LAST}, {IS_LAST*}
{TAGS#}

1.4. Global Variables (gVars) — Data Exchange Between Modules

Normally, each module only parses the data it generated itself (local context). But in complex interfaces, data from one module is often needed in another (for example, the Cart module calculated a total, but it needs to be displayed in the site header).

For this purpose, the global array MELBIS()->gVars is used.

How it works:

  1. In the first module’s code, the developer writes data to the global array: MELBIS()->gVars['cart']['total_sum'] = 1500;
  2. In the second module’s code, the developer can write their own data: MELBIS()->gVars['user']['is_logged'] = 1;
  3. In any HTML template of other nested modules on the page, this data becomes accessible by the top-level key name:

🔥 Important priority rule: Local module variables always “win” over global ones. If the global array has a TITLE key, and the current module also passed a TITLE key, the parser will output the local value from the module. This protects the markup from accidental conflicts.

🔥 Important: Global variables gVars are processed by the same parser as regular data. This means that all template engine capabilities are available for global variables:


{#MENU:ITEMS}
    <a href="{URL}">{NAME}</a>
{MENU:ITEMS#}

2. Logic Blocks (Loops)

Used for iterating over arrays (for example, lists of products, properties, files).

{#STORE}
    <div class="item">
        <b>{NUM1}. {NAME}</b> 
    </div>
{STORE#}

Service variables inside a loop:


3. Conditions (Display Logic)

Condition tags start with * and close the same way.

Basic checks:

Comparison operations (?):

Smart checks (IN and BETWEEN):


4. Data Modifiers (Transformations)

Called using the pipe symbol |. Parameters are passed using a colon :. Modifiers can be chained: {VAR|mod1|mod2:param}.

Text and HTML

Numbers and Math

Dates

Multilingual Formats (intl)

These modifiers automatically adapt output formats to the current page language (MELBIS()->LanguageSet('en')), using the ICU library. Ideal for projects with en, ru, ua, etc.

Arrays and JSON

System Functions

Calling Standard PHP Functions

The parser allows you to pass a variable’s value through any available PHP function (unless it is blocked for security reasons).

How arguments are passed:

Syntax: {VARIABLE|function_name:param2,param3}

Usage examples:

Calling Custom Functions (Module Callbacks)

For complex business logic (for example, string translation, multi-currency support, specific formatting), you can register your own functions. They are called through a modifier starting with the $ sign (for example, |$trans).

The callback mechanism uses strict registration and passes all data to the function as a single associative array, allowing flexible combination of template variables and system settings.

1. Registering Functions in PHP

You have two levels of registration: global and local (at the level of a specific module).

MELBIS()->DefineCallback('trans', 'MELBIS_INC_translate', ['from' => 'en']);
MELBIS()->UnitCallbackCustom('trans', 'MELBIS_INC_translate', ['from' => 'ru']);

2. Calling in an HTML Template

In the template, you specify the alias (with the $ sign) and, if necessary, list additional keys separated by commas — the values of which need to be extracted from the current data context and passed to the function.

Syntax: {MAIN_VARIABLE|$alias:EXTRA_KEY_1,EXTRA_KEY_2}

Example: {TITLE|$trans:ID,CODE}

3. How the Data Array (Payload) for the Function is Assembled and Priorities

When the parser encounters {TITLE|$trans:ID,CODE}, it assembles a single associative array $args_assoc and passes it to your function.

Assembly occurs with priority layering (from lowest to highest):

  1. (Lowest priority) Default system parameters from PHP: First, the base settings specified during registration (DefineCallback or UnitCallbackCustom) are taken. Example: ['lang' => 'en', 'color' => 'black']
  2. (Medium priority) Main variable from the template: The parser takes the variable to the left of | and places it in the array. Example: ['lang' => 'en', 'color' => 'black', 'title' => 'Value']
  3. (Highest priority) Additional keys from the template: The parser looks up the requested additional keys (for example, LANG, CODE) in the current template data. If the template developer explicitly requested a key that already exists in the PHP default settings, the value from the HTML template will completely overwrite the default. Final array: ['lang' => 'ru', 'color' => 'black', 'title' => 'Value', 'code' => 'ABC'] (here lang from the template overrode lang from the PHP settings).

5. Complex Examples

Example 1: SEO Block for a Product Page

Using modifier chains for safe generation of microdata and meta tags.

<title>{PRODUCT_NAME|html} buy for {PRICE|nums} UAH</title>
<meta name="description" content="{DESCRIPTION|plain|short:160}">

<script type="application/ld+json">
{ROW|json}
</script>

Example 2: Product Card with Complex Logic

Checking availability, calculating the discount percentage on the fly, and displaying placeholders.

<div class="product-card {*IS_FIRST}first-item{IS_FIRST*}">
    <img src="{IMAGE_URL|def:/images/no-photo.png}" alt="{PRODUCT_NAME|html}">
    
    <h3>{PRODUCT_NAME}</h3>
    
    {*?PRICE<OLD_PRICE}
        <div class="badge-discount">
            Discount {ROW|calc:100-(price/old_price*100)|nums:0} %
        </div>
        <span class="old-price">{OLD_PRICE|num} UAH</span>
    {PRICE*}
    
    <span class="current-price">{PRICE|num} UAH</span>

    {*?STOCK_STATUS==instock,preorder}
        <button onclick="addToCart({ID|int})">Buy</button>
    {STOCK_STATUS*}
    {*?STOCK_STATUS!=instock,preorder}
        <span class="out-of-stock">Out of stock</span>
    {STOCK_STATUS*}
</div>

Example 3: Order Table in a Personal Account

Using ranges, dates, and loop counters.

<table>
    <tr>
        <th>#</th>
        <th>Date</th>
        <th>Amount</th>
        <th>Status</th>
    </tr>
    {#ORDERS}
    <tr class="{*IS_EVEN}bg-gray{IS_EVEN*}">
        <td>{NUM1}</td>
        <td>{CREATED_AT|date:d M Y}</td>
        <td>{TOTAL|num:2,., } $</td>
        <td>
            {*?STATUS_ID==1-3} <span class="badge-blue">{STATUS_NAME}</span> {STATUS_ID*}
            {*?STATUS_ID==4-5} <span class="badge-green">{STATUS_NAME}</span> {STATUS_ID*}
        </td>
    </tr>
    {ORDERS#}
</table>