Tworzenie modułu Prestashop cz. 3 – wykorzystanie własnych klas i kontrolerów
Poradniki

Tworzenie modułu Prestashop cz. 3 – wykorzystanie własnych klas i kontrolerów

W tym poradniku ponownie wykorzystamy pliki z poprzednich części.

Przygotujmy w pierwszej kolejności kontroler. W tym celu utwórzmy w głównym folderze naszego modułu folder controllers a wewnątrz niego folder admin, tam dodajmy plik myfirstmodule.php

Następnie utwórzmy klasę dla naszych elementów, w tym celu utwórzmy folder classes w głównym folderze modułu, a następnie w nim plik MyFirstModuleBlock.php

Zanim zajmiemy się naszym kontrolerem i klasą, dodajmy dodatkową treść do funkcji installDB() w głównym pliku modułu.

$correct = Db::getInstance()->execute('
            CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'myfirstmodule_blocks` ( 
                `id_myfirstmodule_block` INT UNSIGNED NOT NULL AUTO_INCREMENT ,                 `active` INT NOT NULL ,
                `position` INT NOT NULL ,
                PRIMARY KEY (`id_myfirstmodule_block`)
            ) ENGINE = ' . _MYSQL_ENGINE_ . ';
        ');

        $correct = Db::getInstance()->execute('
            CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'myfirstmodule_blocks_lang` ( 
                `id_myfirstmodule_block_lang` INT UNSIGNED NOT NULL AUTO_INCREMENT , 
                `id_myfirstmodule_block` INT NOT NULL , 
                `id_lang` INT NOT NULL , 
                `name` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL , 
                PRIMARY KEY (`id_myfirstmodule_block_lang`)
            ) ENGINE = ' . _MYSQL_ENGINE_ . ';
        ');

Tymi zapytaniami utworzymy podczas instalacji dwie dodatkowe tabele, będziemy tworzyć bloki a linki będą dodawane do bloków, dlatego musimy usunąć stare tabele i zmodyfikować zapytanie dla tabeli myfirstmodule

$correct = Db::getInstance()->execute('
            CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'myfirstmodule` ( 
                `id_myfirstmodule` INT UNSIGNED NOT NULL AUTO_INCREMENT , 
                `id_myfirstmodule_block` INT NOT NULL ,
                `blank` INT NOT NULL ,
                PRIMARY KEY (`id_myfirstmodule`)
            ) ENGINE = ' . _MYSQL_ENGINE_ . ';
        ');

Kolumna id_shop została zmieniona na id_myfirstmodule_block

Musimy jeszcze dodać kod do funkcji install() za pomocą którego wyświetlimy własny odnośnik w menu Panelu Administracyjnego.

$tab = new Tab();
$tab->active = 1;
$tab->class_name = 'MyFirstModule';
$tab->position = 3;
$tab->name = array();
foreach (Language::getLanguages(true) as $lang) {
     $tab->name[$lang['id_lang']] = 'Mój pierwszy moduł';
}
$tab->id_parent = (int) Tab::getIdFromClassName('CONFIGURE');
$tab->module = $this->name;
$tab->add();
$tab->save();

Wymienione w tym miejscu pola to:

  • active – czy nasz odnośnik ma być aktywy
  • class_name – klasa odnośnika
  • position – pozycja na liście
  • name – to musi być tablica z wariantem dla każdego języka, dlatego niżej można zobaczyć pętlę foreach
  • id_parent – id rodzica, uzyskujemy je za pomocą funkcji getIdFromClassName, gdzie w panelu dostępne są 3 główne zakładki: SELL (Sprzedaż), IMPROVE (Ulepszenia), CONFIGURE (Konfiguruj), ponadto jeżeli chcemy dodać zakładkę do któregoś rozwijanego menu, to w tym momencie należy podać odpowiednią klasę dla tej zakładki
  • module – nazwa modułu, do którego odnosi zakładki

Niżej wywoływane są dwie funkcje, jedna dodaje zakładkę, druga zapisuje ustawienia.

Otwórzmy teraz nasz plik kontrolera i uzupełnijmy go następującą treścią:

<?php

include_once(_PS_MODULE_DIR_.'myfirstmodule/classes/MyFirstModuleBlock.php');

class myfirstmoduleController extends AdminController
{
    protected $position_identifier = 'id_myfirstmodule_block';

    public function __construct()
    {
        $this->bootstrap = true;
        $this->table = 'myfirstmodule_blocks';
        $this->list_id = 'myfirstmodule_blocks';
        $this->identifier = 'id_myfirstmodule_block';
        $this->className = 'MyFirstModuleBlock';
        $this->lang = true;
        $this->_defaultOrderBy = 'position';

        parent::__construct();

        $this->fields_list = array(
            'id_myfirstmodule_block' => array(
                'title' => $this->trans('ID', array(), 'Admin.Global'),
                'align' => 'center',
                'class' => 'fixed-width-xs',
            ),
            'name' => array(
                'title' => $this->trans('Name', array(), 'Admin.Global'),
                'filter_key' => 'b!name',
                'align' => 'left',
            ),
            'position' => array(
                'title' => $this->trans('Position', array(), 'Admin.Global'),
                'filter_key' => 'a!position',
                'position' => 'position',
                'align' => 'center',
                'class' => 'fixed-width-xs',
            ),
            'active' => array(
                'title' => $this->trans('Displayed', array(), 'Admin.Global'),
                'align' => 'center',
                'active' => 'status',
                'class' => 'fixed-width-sm',
                'type' => 'bool',
                'orderby' => false,
            ),
        );

        $this->addRowAction('edit');
        $this->addRowAction('delete');

        $this->bulk_actions = array(
            'delete' => array(
                'text' => $this->trans('Delete selected', array(), 'Admin.Notifications.Info'),
                'icon' => 'icon-trash',
                'confirm' => $this->trans('Delete selected items?', array(), 'Admin.Notifications.Info'),
            ),
        );
    }

    public function initPageHeaderToolbar()
    {
        if (empty($this->display)) {
            $this->page_header_toolbar_btn['new_block'] = array(
                'href' => $this->context->link->getAdminLink('myfirstmodule', true, array(), array(' addmyfirstmodule_blocks' => 1)),
                'desc' => 'Dodaj nowy blok',
                'icon' => 'process-icon-new',
            );
        }

        parent::initPageHeaderToolbar();
    }
}

Jest tutaj do opisania wiele pól, na początku możemy zauważyć, że w funkcji __construct() znajdziemy wiele ustawień:

  • bootstap – czy chcemy wykorzystywać bootstrap w szablonach
  • table – tabela, z której mają być pobierane wartości, jeśli istnieje tabela z nazwą głównej tabeli i dopiskiem ­­_lang to jej wartości również zostaną pobrane, tak jak w naszym przypadku myfirstmodule_blocks i tabela myfirstmodule_blocks_lang
  • list_id – id naszej listy w widoku
  • identifier – po jakim polu w bazie mają być rozpoznawane poszczególne elementy
  • className – nazwa klasy PHP dla widoku
  • lang – czy dostępna wielojęzyczność
  • _defaultOrderBy – domyślne sortowanie po
  • fields_list – lista kolumn w tabeli, jako indeksy w tablicy podajemy nazwy kolumn z bazy danych, dostępne atrybuty to:
    • title – tytuł kolumny
    • align – wyrównanie tekstu w kolumnie
    • class – klasa kolumny
    • filter_key – jaka wartość ma być wyświetlana, czasem, jeśli połączone jest kilka tabel to mogą dublować się nazwy kolumn, tutaj precyzujemy z której tabeli dane chcemy pobierać, tabela „a” jest główna, „b” to ­_lang
    • type – rodzaj pola, w przypadku bool wyświetla się „X” lub „fajka”
    • orderby – jeśli ustawione na false, nie da się sortować po tym polu
  • addRowAction – tą funkcją dodajemy dostępne akcje do każdej linii w tabeli
  • bulk_actions – są to akcje dostępne po zaznaczeniu elementów na liście, my dodaliśmy tylko możliwość usuwania wielu naraz

Niżej możemy znaleźć kolejną funkcję jaką jest initPageHeaderToolbar w której ustawiamy zawartość górnego paska, możemy dodać tam dodatkowe przyciski. W tym przypadku dodajemy przycisk przenoszący do formularza dodawania nowego bloku.

Gdy wszystko jest przygotowane poprawnie powinniśmy zobaczyć po instalacji zakładkę Mój pierwszy modułw zakładce Konfiguruj, po przejściu do niej zobaczymy widok jak powyżej.

Przejdźmy teraz do przygotowania widoku dodawania bloku w naszym kontrolerze.

W tym celu musimy przygotować nasz plik klasy, otwórzmy go i dodajmy tam następującą treść:

<?php

class MyFirstModuleBlock extends ObjectModel {
    public $id;
    public $id_myfirstmodule_blocks;
    public $name;
    public $position;
    public $active;

    public static $definition = array(
        'table' => 'myfirstmodule_blocks',
        'primary' => 'id_myfirstmodule_block',
        'multilang' => true,
        'multilang_shop' => true,
        'fields' => array(
            'position' => array('type' => self::TYPE_INT),
            'active' => array('type' => self::TYPE_BOOL),

            'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 512),
        ),
    );
}

Są to ustawienia modelu, na podstawie którego będą się dodawać, aktualizować i usuwać dane.

Na początku ustalamy po prostu jakie pola będą dostępne, niżej znajdziemy zmienną definitione, gdzie znajdziemy takie ustawienia jak:

  • table – główna tabela dla elementu
  • primary – główna kolumna, za pomocą, której elementy są rozróżniane
  • multilang – czy istnieje tablica z treścią dla języków
  • multilang_shop – czy w tablicy dla języków rozpoznawane są sklepy (czyli czy istnieje kolumna id_shop
  • fields – pola które są przechowywane przez bazę danych, a dla nich możemy zobaczyć takie ustawienia jak:
    • type – rodzaj pola
    • lang – jeśli true to zmienna znajduje się w tabeli _lang
    • validate – na jakiej podstawie ma odbywać się walidacja
    • size – rozmiar pola

Po zapisaniu ustawień, możemy już dodawać nasz blok w panelu, a on od razu wyświetli się na liście. Możemy go też również od razu usuwać i edytować, także mamy przygotowaną obsługę wszystkiego w dużo szybszy sposób.

Nie działa jednak jeszcze zmienianie pozycji, a dodając elementy można zauważyć, że każdy ma pozycję 1.

Aby to naprawić dodajmy do naszej klasy dwie funkcje:

public function add($autoDate = true, $nullValues = false)
    {
        $this->position = MyFirstModuleBlock::getLastPosition();

        return parent::add($autoDate, true);
    }

    public static function getLastPosition()
    {
        $sql = '
        SELECT MAX(position) + 1
        FROM `' . _DB_PREFIX_ . 'myfirstmodule_blocks`';

        return Db::getInstance()->getValue($sql);
    }

Pierwsza funkcja jest wywoływana w momencie dodawania bloku do bazy danych, w tym miejscu ją nadpisujemy i ustawiamy parametr position równy temu co zwróci kolejna funkcja getLastPosition() w której pobieramy z bazy danych najwyższą pozycję + 1

To pozwoli na poprawne ustawianie się pozycji nowych elementów, przeciąganie jednak niczego nie zmienia, musimy dodać funkcje do naszego kontrolera:

public function ajaxProcessUpdatePositions()
    {
        $way = (int)Tools::getValue('way');
        $id_myfirstmodule_block = (int)Tools::getValue('id');
        $positions = Tools::getValue('myfirstmodule_block');

       if (is_array($positions)){
           foreach ($positions as $position => $value)
           {
               $pos = explode('_', $value);

               if (isset($pos[2]) &amp;&amp; (int)$pos[2] === $id_myfirstmodule_block)
               {
                       if (isset($position) &amp;&amp; $this->updatePosition($way, $position, $id_myfirstmodule_block))
                           echo 'ok';
                       else
                           echo '{"hasError" : true, "errors" : "Can not update id '.(int)$id_myfirstmodule_block.' to position '.(int)$position.' "}';
                   
                   break;
               }
           }
       }
    }

    public function updatePosition($way, $position, $id)
    {
        
        if (!$res = Db::getInstance()->executeS('
            SELECT `id_myfirstmodule_block`, `position`
            FROM `'._DB_PREFIX_.'myfirstmodule_blocks`
            ORDER BY `position` ASC'
        ))
            return false;

        foreach ($res as $block)
            if ((int)$block['id_myfirstmodule_block'] == (int)$id)
                $moved_block = $block;

        if (!isset($moved_block) || !isset($position))
            return false;
        var_dump($moved_block['position']);
        
        return (Db::getInstance()->execute('
            UPDATE `'._DB_PREFIX_.'myfirstmodule_blocks`
            SET `position`= `position` '.($way ? '- 1' : '+ 1').'
            WHERE `position`
            '.($way
                ? '> '.(int)$moved_block['position'].' AND `position` <= '.(int)$position
                : '< '.(int)$moved_block['position'].' AND `position` >= '.(int)$position.'
            '))
        &amp;&amp; Db::getInstance()->execute('
            UPDATE `'._DB_PREFIX_.'myfirstmodule_blocks`
            SET `position` = '.(int)$position.'
            WHERE `id_myfirstmodule_block` = '.(int)$moved_block['id_myfirstmodule_block']));
    }

Jest to już z góry gotowe rozwiązanie, wykorzystywane też przy głównych funkcjach Prestashop. Pierwsza funkcja jest wywoływana w momencie zmiany pozycji poprzez ajax, przesyłane są wszystkie zmienione pozycje, każdego elementu, gdzie ta zmiana zachodzi.

Dla każdego elementu jest uruchamiana funkcja updatePosition która aktualizuje te pozycje w bazie danych.

W ten sposób mamy ukończony prosty CRUD z wykorzystaniem kontrolera i modelu, jest to na pewno szybkie rozwiązanie.

Przygotujmy teraz możliwość dodawania odnośników do bloków. W tym celu musimy w funkcji __construct dodać linijkę:

$this->addRowAction('view');

Dzięki czemu na liście będzie pojawiał się przycisk „Zobacz”

Następnie musimy dodać do naszego folderu classes nową klasę o nazwie MyFirstModuleLink.php i umieśćmy wewnątrz taki kod:

<?php

class MyFirstModuleLink extends ObjectModel {
    public $id;
    public $id_myfirstmodule;
    public $id_myfirstmodule_block;
    public $name;
    public $url;
    public $blank;

    public static $definition = array(
        'table' => 'myfirstmodule',
        'primary' => 'id_myfirstmodule',
        'multilang' => true,
        'multilang_shop' => true,
        'fields' => array(
            'blank' => array('type' => self::TYPE_BOOL),
            'id_myfirstmodule_block' => array('type' => self::TYPE_BOOL),

            'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 512),
            'url' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 512),
        ),
    );
}

Jest to po prostu konfiguracja na tej samej zasadzie co w poprzedniej.

Otwórzmy teraz plik kontrolera i dodajmy do funkcji __construct­ na górze treść:

$this->mode = 'block';

Następnie usuńmy tą treść, będziemy ją umieszczać w innym miejscu.

$this->addRowAction('view');
$this->addRowAction('edit');
$this->addRowAction('delete');

I dodajmy na samym dole funkcji:

$this->changeClass = false;

        if (Tools::isSubmit('addmyfirstmodule') || Tools::isSubmit('viewmyfirstmodule_blocks') || Tools::isSubmit('submitAddmyfirstmodule') || Tools::isSubmit('updatemyfirstmodule') || Tools::isSubmit('deletemyfirstmodule')) {
            if(Tools::isSubmit('addmyfirstmodule') || Tools::isSubmit('submitAddmyfirstmodule')){
                $this->display = 'add';
                $this->changeClass = true;
            }

            if(Tools::isSubmit('updatemyfirstmodule')){
                $this->display = 'update';
                $this->changeClass = true;
            }

            if(Tools::isSubmit('deletemyfirstmodule')){
                $this->changeClass = true;
            }
            
            if($this->changeClass){
                $this->table = 'myfirstmodule';
                $this->className = 'MyFirstModuleLink';
                $this->identifier = 'id_myfirstmodule';
                $this->position_identifier = 'id_myfirstmodule';
                $this->position_group_identifier = 'id_myfirstmodule_block';
                $this->list_id = 'links_values';
                $this->lang = true;
            }

            $this->mode = 'link';
        }

W tym miejscu sprawdzamy czy aktualnie jest realizowane zadanie dotyczące linku, jeśli tak, to zmieniamy nazwę tabeli na ta posiadającą linki, tak samo zmieniamy nazwę klasy itd. Zmienną $mode będziemy wykorzystywać później.

Nie zmieniamy klasy dla widoku viewmyfirstmodule_block ponieważ wywołuje to błąd musimy dokonać tej zmiany w innym miejscu.

Przejdźmy teraz do funkcji initPageHeaderToolbar, dodajmy tam treść:

 else {
            if (($id = (int) Tools::getValue('id_myfirstmodule_block'))) {
                $this->page_header_toolbar_btn['new_value'] = array(
                    'href' => self::$currentIndex . '&amp;addmyfirstmodule&amp;id_myfirstmodule_block=' . $id . '&amp;token=' . $this->token,
                    'desc' => 'Dodaj nową wartość',
                    'icon' => 'process-icon-new',
                );
            }
        }

Dzięki czemu w górnym menu będzie wyświetlać się przycisk pozwalający dodać wartość.

Teraz dodajmy dwie nowe funkcje do naszego kontrolera:

public function renderView()
    {
        if (($id = (int) Tools::getValue('id_myfirstmodule_block'))) {
            $this->table = 'myfirstmodule';
            $this->className = 'MyFirstModuleLink';
            $this->identifier = 'id_myfirstmodule';
            $this->position_identifier = 'id_myfirstmodule';
            $this->position_group_identifier = 'id_myfirstmodule_block';
            $this->list_id = 'links_values';
            $this->lang = true;

            $this->context->smarty->assign(array(
                'current' => self::$currentIndex . '&amp;id_myfirstmodule_block=' . (int) $id . '&amp;viewmyfirstmodule_blocks',
            ));

            $this->fields_list = array(
                'id_myfirstmodule' => array(
                    'title' => $this->trans('ID', array(), 'Admin.Global'),
                    'align' => 'center',
                    'class' => 'fixed-width-xs',
                ),
                'name' => array(
                    'title' => $this->trans('Nazwa', array(), 'Admin.Catalog.Global'),
                    'width' => 'auto',
                    'filter_key' => 'b!name',
                ),
                'url' => array(
                    'title' => $this->trans('Adres URL', array(), 'Admin.Catalog.Global'),
                    'width' => 'auto',
                    'filter_key' => 'b!url',
                ),
                'blank' => array(
                    'title' => $this->trans('Nowa karta', array(), 'Admin.Catalog.Global'),
                    'width' => 'auto',
                    'type' => 'bool',
                    'orderby' => false,
                ),
            );

            $this->_where = 'AND a.`id_myfirstmodule_block` = ' . (int) $id;
            $this->_orderBy = 'id_myfirstmodule';

            self::$currentIndex = self::$currentIndex . '&amp;id_myfirstmodule_block=' . (int) $id . '&amp;viewmyfirstmodule_blocks';
            $this->processFilter();

        } else {
            $this->addRowAction('view');
        }

        $this->addRowAction('edit');
        $this->addRowAction('delete');

        return parent::renderList();
    }

    public function renderList()
    {
        if($this->mode != 'link'){
            $this->addRowAction('view');
        }

        $this->addRowAction('edit');
        $this->addRowAction('delete');
        
        return parent::renderList();
    }

Pierwsza pozwala przygotować inną tabelę dla naszych elementów, składnia taka sama jak poprzednio w funkcji __construct, z tym wyjątkiem, że nie dodajemy przycisku Zobacz do tej listy.

Druga funkcja odpowiada za wyświetlanie na stronie głównej modułu po zaktualizowaniu bloku, jest to osoba funkcja, dlatego musimy tutaj tylko dodać przyciski, reszta jest brana na podstawie ustawień z funkcji __construct

Zmodyfikujmy kolejno funkcje renderForm:

if($this->mode == 'link'){

            $this->fields_form = array(
                'tinymce' => true,
                'legend' => array(
                    'title' => $this->l('Link'),
                    'icon' => 'icon-folder-close',
                ),
                'input' => array(
                    array(
                        'type' => 'text',
                        'label' => $this->l('Tekst odnośnika'),
                        'name' => 'name',
                        'lang' => true,
                        'size' => 20,
                        'required' => true
                    ),
                    array(
                        'type' => 'text',
                        'label' => $this->l('Adres odnośnika'),
                        'name' => 'url',
                        'lang' => true,
                        'size' => 20,
                        'required' => true
                    ),
                    array(
                        'type' => 'switch',
                        'label' => $this->l('Nowa karta'),
                        'name' => 'blank',
                        'is_bool' => true,
                        'required' => true,
                        'values' => array(
                            array(
                                'id' => 'label3_on',
                                'value' => 1,
                                'label' => $this->l('Tak')
                            ),
                            array(
                                'id' => 'label3_off',
                                'value' => 0,
                                'label' => $this->l('Nie')
                            )
                        )
                    ),
                    array(
                        'type' => 'hidden',
                        'name' => 'id_myfirstmodule_block',
                        'value' => (int) Tools::getValue('id_myfirstmodule_block')
                    )
                ),
                'submit' => array(
                    'title' => $this->trans('Save', array(), 'Admin.Actions'),
                ),
            );

        } else {

Dodajemy instrukcję warunkową, jeśli $mode == ‘link’ dzięki czemu wyświetlamy inny formularz dla linków. Wewnątrz else umieszczamy poprzednią zawartość funkcji.

Pozostało nam dodać dwie funkcje:

public function processAdd(){
        if(Tools::isSubmit('submitAddmyfirstmodule')){
            $this->redirect_after = self::$currentIndex . '&amp;viewmyfirstmodule_blocks=&amp;id_myfirstmodule_block=' . (int) Tools::getValue('id_myfirstmodule_block') . '&amp;token=' . $this->token;
        }

        return parent::processAdd();
    }

    public function processUpdate()
    {
        if(Tools::isSubmit('submitAddmyfirstmodule')){
            $this->redirect_after = self::$currentIndex . '&amp;viewmyfirstmodule_blocks=&amp;id_myfirstmodule_block=' . (int) Tools::getValue('id_myfirstmodule_block') . '&amp;token=' . $this->token;
        }

        return parent::processUpdate();
    }

Uruchamiają się kolejno po dodaniu i po zaktualizowaniu w bazie danych. Ustawiamy tylko parametr, gdzie ma przekierować po zapisaniu, ponieważ domyślnie przenosi na stronę główną modułu.

Gdy wszystko w porządku zobaczymy taki widok i będziemy mogli dodawać/edytować/usuwać wartości.

Z pliku modułu usuwamy nie potrzebną treść, zostawiamy tylko formularz $mode == 0 dotyczący konfiguracji i tak samo zapis tego pola, pozostałości po dodawaniu odnośników można usunąć, musimy jednak zmodyfikować plik tak, aby zmienić widok na stronie głównej.

Dodajmy więc najpierw dwa bloki do bazy a do nich po dwa odnośniki, a następnie przejdźmy do pliku modułu myfirstmodule.php

Zmieńmy treść funkcji hookDisplayHome na następującą:

$id_shop = (int)Context::getContext()->shop->id;
        $id_lang = (int)Context::getContext()->language->id;

        $blocksArray = [];

        $blocks = Db::getInstance()->ExecuteS("
            SELECT * FROM `" . _DB_PREFIX_ . "myfirstmodule_blocks` INNER JOIN `" . _DB_PREFIX_ . "myfirstmodule_blocks_lang` ON " . _DB_PREFIX_ . "myfirstmodule_blocks.id_myfirstmodule_block = " . _DB_PREFIX_ . "myfirstmodule_blocks_lang.id_myfirstmodule_block WHERE `id_shop` = " . $id_shop . " AND `id_lang` = " . $id_lang . ' ORDER BY `position` ASC'
        );

        foreach($blocks as $block){
            $block['elements'] = Db::getInstance()->ExecuteS("
                SELECT * FROM `" . _DB_PREFIX_ . "myfirstmodule` INNER JOIN `" . _DB_PREFIX_ . "myfirstmodule_lang` ON " . _DB_PREFIX_ . "myfirstmodule.id_myfirstmodule = " . _DB_PREFIX_ . "myfirstmodule_lang.id_myfirstmodule WHERE `id_shop` = " . $id_shop . " AND `id_lang` = " . $id_lang . ' AND `id_myfirstmodule_block` = ' . $block['id_myfirstmodule_block']
            );

            $blocksArray[] = $block;
        }

        $this->context->smarty->assign(
            array(
                'myfirstmodule' => Configuration::get('myfirstmodule'),
                'blocks' => $blocksArray
            )
        );
        return $this->display(__FILE__, 'views/front/myfirstmodule.tpl');

Teraz pobierane będą bloki a dla bloków elementy. Przejdźmy do pliku views/front/myfirstmodule.php

I zmieńmy treść na:

<h3>{$myfirstmodule}</h3>

{if count($blocks) > 0}
    <div class="row" style="margin-top: 50px;">
        {foreach from=$blocks item=block}
        <div class="col-md-3">
            <h5>{$block.name}</h5>
            <ul>
                {foreach from=$block.elements item=element}
                    <li><a href="{$element.url}" {if $element.blank == 1} target="_blank" {/if}>{$element.name}</a></li>
                {/foreach}
            </ul>
        </div>
        {/foreach}
    </div>
{/if}

Będziemy wyświetlać bloki a wewnątrz nich elementy.

Jeśli wszystko jest w porządku powinniśmy zobaczyć taki widok jak powyżej.

W powyższy sposób przygotowaliśmy rozbudowany moduł z użyciem kontrolera, możemy oczywiście tworzyć wiele kontrolerów z wieloma widokami.

Nasza Strona internetowa wykorzystuje pliki cookies oraz podobne technologie w celi świadczenia Państwu usług z zachowaniem najwyższych standardów, w tym w sposób dostosowany do indywidualnych potrzeb. Korzystanie ze Strony bez zmiany ustawień dotyczących cookies. Więcej szczegółów w naszej „Polityce Prywatności”
Więcej Akceptuję