Работа модульного скрипта

Теперь мы готовы рассмотреть с Вами структуру типового модульного скрипта витрины. Мы выбрали для примера melbis_store_topic.php. Его задача - вывести список товаров соответствующего раздела магазина.

<?php
/***************************************************************************************************
 * @version 6.3.0
 * @copyright 2019 Melbis Company
 * @link https://melbis.com
 * @author Dmitriy Kasyanoff
 **************************************************************************************************/

Заголовок скрипта - описание задач, имя автора и его реквизиты.

/** 
 * Function MELBIS_STORE_TOPIC
 **/
function MELBIS_STORE_TOPIC($mVars)
{  

Определение главной функции модульного скрипта, ту которую будет вызывать парсер. Главную функцию всегда легко узнать, потому что она полностью соответствует названию модуля в верхнем регистре. В качестве входных параметров ей передается ассоциативный массив с входными переменными. Имена переменных (ключи массива) соответствуют тем, что были указаны во втором поле над редактором модуля. В нашем случае, передается один параметр topic_id типа int, соответственно обращение к нему будет выглядеть так: $mVars['topic_id'].

    global $gParser;
                       
    // Create 
    $tpl = $gParser->TplCreate();

В первой строке мы устанавливаем связь с нашим парсером, который содержит удобные инструменты для работы с данными. И далее создаем указатель локального парсера для того, чтобы сформировать результат работы модуля на основе его HTML-шаблонов.

    // Get random goods
    $id = $mVars['topic_id']*1;
    $command = "SELECT s.id
                  FROM {DBNICK}_store s
                  JOIN {DBNICK}_topic_store ts
                    ON s.id = ts.store_id
                 WHERE s.no_visible = 0
                   AND ts.topic_id = '$id'
              ORDER BY ts.pos
                 LIMIT 1000 
                ";                    
    $goods = $gParser->SqlSelect(__LINE__, $command);
    if ( count($goods) == 0 ) $gParser->TplAssign($tpl, 'ITEM', '');
    foreach ( $goods as $item )
    {
        $gParser->TplAssign($tpl, 'ID', $item['id']);
        $gParser->TplParse($tpl, 'ITEM', '.item');  
    }    

Далее мы обращаемся к базе данных, чтобы получить список товаров из раздела с ID, которое мы получили как входной параметр. Для этого мы используем специальные методы парсера для работы с базой данных. Узнать полный перечень методов парсера можно в разделе "API Parser". Получив данные из базы, мы перебираем их в цикле для того, чтобы с помощью шаблонов этого модуля сформировать HTML-код. 

    // Final: return content
    $gParser->TplParse($tpl, 'MAIN', 'main');

    return $gParser->TplFree($tpl, 'MAIN');
} 

?>

Завершающая часть модуля, в которой мы собираем весь HTML-код в шаблон с именем MAIN и возвращаем сформированный результат стандартной функцией return.

Выше мы рассмотрели работу модульного скрипта melbis_store_topic.php. Однако, в нем нет ничего, что касается HTML-кода, который лежит в основе всех страниц интернет-сайтов. В этом нет ничего удивительного. Существующая общепринятая практика программирования разделяет сложную задачу формирования витрины магазина на составляющие: кто-то занимается работой с данными - логикой магазина, а кто-то на основе данных выстраивает HTML-код страницы - верстает дизайн магазина. Это так называемый паттерн или концепция MVC (в нашем случае, моделью выступает модуль скрипта, формирующий данные, контроллером - парсер, который занимается вопросами ввода/вывода, а за представление отвечают HTML-шаблоны). Теоретически, в скрипте модуля не должно находиться никакого кода, который бы помогал формировать HTML-код. Однако наша практика показывает, что модуль все же должен принимать элементарное участие в компоновке данных. Впрочем, надо заметить, что если Вы приверженец строгой модели MVC в программировании, то Вы без проблем сможете разрабатывать сайт и таким образом. Итак, давайте на примерах разберем оба варианта формирования HTML-кода.

Использование локальных ключей

Давайте вернемся еще раз к модулю melbis_store_topic.php. В нем есть обращение к базе данных, в результате чего мы получаем ответ со списком товаров:

    $id = $mVars['topic_id']*1;
    $command = "SELECT s.id
                  FROM {DBNICK}_store s
                  JOIN {DBNICK}_topic_store ts
                    ON s.id = ts.store_id
                 WHERE s.no_visible = 0
                   AND ts.topic_id = '$id'
              ORDER BY ts.pos
                 LIMIT 1000 
                ";                    
    $goods = $gParser->SqlSelect(__LINE__, $command);

Теперь можно сформировать HTML-код этого списка, и в этом нам помогут HTML-шаблоны, а также специальные методы парсера для сборки фрагментов HTML-кода в единое целое. Обратите внимание, что у каждого модуля, есть свои HTML-шаблоны и их может быть практически сколько угодно. В нашем случае, для модуля определены следующие HTML-шаблоны:

  • main.htm - содержит фрагмент HTML-кода, формирующий список (заголовок и обрамление)
  • item.htm - содержит фрагмент HTML-кода, формирующий элемент с товаром

Давайте посмотрим на содержимое шаблона main.htm

<div class="container">
    <div class="row melbis_goods">
        {ITEM}
    </div>
</div>

Обратите внимание на ключ {ITEM}. Это и есть локальный ключ в шаблоне, на место которого будет подставляться необходимый нам блок товара, а точнее, шаблон товара item.htm:

<div class="col-lg-4 col-md-6 mb-3">
    
</div>

А вот в этом шаблоне, произойдет вызов другого модуля melbis_store_card.php, который отвечает за формирование товарного блока. Для того чтобы определить какой именно товар надо отобразить, используется ключ [ID]. Учитывая что данный ключ находится внутри другого ключа, то мы решили осуществить его безопасный вызов, заменив фигурные скобки, квадратными (подробнее об этом ниже). Таким образом, благодаря использованию локальных ключей Вы сможете путем подстановок собрать необходимый код из фрагментов. Важной особенностью является то, что на место ключа может быть подставлено либо конкретное значение, либо целый фрагмент HTML-кода. Делается это с помощью специальных методов парсера: TplAssign и TplParse (более подробнее см. "API Parser"):

    foreach ( $goods as $item )
    {
        $gParser->TplAssign($tpl, 'ID', $item['id']);
        $gParser->TplParse($tpl, 'ITEM', '.item');  
    }    

Еще один важный аспект - это отличие ключей в фигурных и в квадратных скобках. Дело в том, что на место ключей в фигурных скобках подставляются данные точь-в-точь такие же, какими они были определены в модуле. На место же ключа в квадратных скобках подставляются данные, которые переводятся в безопасное представление (PHP-функция urlencode()). Например, посмотрите шаблон модуля melbis_store_card.php:

<div class="card h-100 shadow-sm">
            
    
    Place for image
                                                        
      
    <div class="card-body">
        <h5 class="card-title">{NAME}</h5>
        <p class="card-text">{INTRO}</p>    
        <div class="d-flex justify-content-around align-items-center">
            <div class="text-center">    
                <p class="lead m-0">${PRICE}</p>
                <p class="badge badge-warning m-0">{STATUS}</p>                                                                          
            </div>
            <button class="btn btn-primary melbis_btn_add" data-id="{ID}"><i class="fas fa-cart-plus"></i> Add to Basket</button>
        </div>
    </div>   
    <div class="card-footer d-flex justify-content-between">  
        <small class="text-muted">Last updated {UPDATE}</small>
        <span class="badge badge-info float-right">Code: {CODE}</span>
    </div>  
</div>

Использование AJAX + JSON + DOM

Как уже упоминалось выше, есть еще один способ формирования витрины, когда модуль не участвует вообще в формировании конечного HTML-кода витрины. Однако, в этом случае, за счет дополнительного абстрагирования разработка несколько усложняется. Тем не менее, есть ситуации, когда это действительно оправдано. 

Как же будет выглядеть код в модуле, который формирует только готовый набор данных? Давайте рассмотрим модульный скрипт melbis_web_test.php, в нем присутствует такая функция:

/** 
 * Function MELBIS_WEB_TEST_get_cataloge
 **/
function MELBIS_WEB_TEST_get_cataloge($mUserId, $mVars)
{ 
    global $gParser;                                       
    
    // Vars
    $limit = $mVars['post']['limit']*1;
    $offset = $mVars['post']['offset']*1;                           
    $order = ( $mVars['post']['order'] == 'asc' ) ? 'ASC' : 'DESC';
    $sort = ( $mVars['post']['sort'] == '' ) ? 'id' : addslashes($mVars['post']['sort']);
        
    // Get data      
    $command = "SELECT t.id, t.name, t.tlevel, COUNT(ts.id) AS amount
                  FROM {DBNICK}_topic t
             LEFT JOIN {DBNICK}_topic_store ts
                    ON t.id = ts.topic_id           
              GROUP BY t.id    
              ORDER BY $sort $order                 
                ";  
    $data = $gParser->SqlSelectLimit(__LINE__, $command, $offset, $limit);
    
    return json_encode($data);                            
}        

В данном варианте, мы вновь делаем запрос к базе данных (чтобы получить список разделов) и, сразу же кодируем и передаем ответ, используя технологию JSON: json_encode($data). Этот ответ с данными получает JS-скрипт, который до этого собственно и выполнил запрос к этому же модулю по технологии AJAX (дополнительно мы используем библиотеку jQuery с плагином Bootstrap Table):

    $('#melbis_table_cataloge').bootstrapTable({
        columns: columns, 
        toolbar: '#melbis_toolbar_cataloge',
        detailView: true,                           
        detailFormatter: 'melbis_detail_cataloge',           
        url: '/?mod=[MELBIS:MOD]',        
        method: 'post',          
        contentType: 'application/x-www-form-urlencoded',
        queryParams: function (params)
            {    
                params = $.extend(params, 
                { 
                    func: 'get_cataloge', 
                });
        
                return params;
            },               
        pagination: true,
        sidePagination: 'server',  
        pageSize: 20,                 
        pageList: [5, 10, 20, 50, 100, 200],
        uniqueId: 'id',
        sortable: true,
        sortName: 'absindex',
        sortOrder: 'asc',
        showRefresh: true,                
        showColumns: true,    
        striped: true        
    });

Таким образом, мы получаем готовый набор данных, а потом визуализируем их как таблицу на странице. Для этого используется DOM-интерфейс, совместно с JavaScript. Для облегчения работы с ним, как правило, используется библиотеки типа JQuery или отдельные UI-фреймворки, например: Bootstrap, AngularJS, React и т.п.