Kategorie
Poradniki

Przenoszenie treści między sklepami Multistore Prestashop

Jak przenieść dane między sklepami w wersji multistore.

Kategorie
Poradniki

Tworzenie modułu Prestashop odmieniającego imię w mailach

Na początek utwórzmy folder naszego modułu, nazwijmy go namevariations i umieśćmy tam podstawową strukturę plików i folderów:

  • config.xml
  • namevariations.php
  • classes/NameVariation.php
  • controllers/admin/NameVariations.php

W pierwszej kolejności otwórzmy plik config.xml i przygotujmy konfigurację naszego modułu:

<?xml version="1.0" encoding="UTF-8" ?>
<module>
    <name>namevariations</name>
    <displayName><![CDATA[Warianty imion]]></displayName>
    <version><![CDATA[1.0.0]]></version>
    <description><![CDATA[Ustaw warianty dla imion do wykorzystania w szablonach maili.]]></description>
    <author><![CDATA[Akademia sklepów]]></author>
    <is_configurable>0</is_configurable>
    <need_instance>0</need_instance>
    <limited_countries></limited_countries>
</module>

Parametr is_configurable został ustawiony na „0” z uwagi na to, że nie będziemy korzystać ze strony konfiguracji a jedynie z kontrolera.

Przejdźmy teraz do pliku namevariations.php i uzupełnijmy go następującą treścią:

<?php

if (!defined('_PS_VERSION_'))
    exit();

class nameVariations extends Module
{
    public function __construct()
    {
        $this->name = 'namevariations';
        $this->version = '1.0.0';
        $this->author = 'Akademia Sklepów';
        $this->need_instance = 0;
        $this->ps_versions_compliancy = array('min' => '1.7.1.0', 'max' => _PS_VERSION_);
        $this->bootstrap = true;
        $this->is_configurable = false;

        parent::__construct();

        $this->displayName = $this->l('Warianty imion', 'myfirstmodule');
        $this->description = $this->l('Ustaw warianty dla imion do wykorzystania w szablonach maili.', 'myfirstmodule');
    }

    public function install()
    {
        if (
            !parent::install()
            || !$this->installDB()
        ) {
            return false;
        }

        $tab = new Tab();
        $tab->active = 1;
        $tab->class_name = 'NameVariations';
        $tab->position = 3;
        $tab->name = array();
        foreach (Language::getLanguages(true) as $lang) {
            $tab->name[$lang['id_lang']] = 'Odmiany Imion';
        }
        $tab->id_parent = (int) Tab::getIdFromClassName('CONFIGURE');
        $tab->module = $this->name;
        $tab->add();
        $tab->save();

        return true;
    }

    public function installDB(){
        $correct = true;

        $correct = Db::getInstance()->execute('
            CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'namevariations` ( 
                `id_namevariation` INT UNSIGNED NOT NULL AUTO_INCREMENT , 
                `name` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL , 
                `dopelniacz` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL , 
                `celownik` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL , 
                `biernik` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL , 
                `narzednik` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL , 
                `miejscownik` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL , 
                PRIMARY KEY (`id_namevariation`)
            ) ENGINE = ' . _MYSQL_ENGINE_ . ';
        ');

        return $correct;
    }

    public function uninstall()
    {
        return parent::uninstall();
    }
}

Wewnątrz funkcji __construct ustalamy konfigurację, parametr is_configurable ustawiamy w tym miejscu na false

Wewnątrz funkcji install wywołujemy funkcję instalującą bazę danych a następnie tworzymy nową zakładkę w menu bocznym panelu administracyjnego, wewnątrz zakładki Ustawienia

Kolejno przygotowujemy funkcję installDB w której tworzymy tabelę dla naszego modułu przechowującą podstawowe imię oraz jego odmiany.

Ostatnia funkcja to uninstall za pomocą, której odinstalowujemy nasz moduł.

Przejdźmy do pliku classes/NameVariaton.php uzupełnijmy plik następującą treścią:

<?php

class NameVariation extends ObjectModel {
    public $id;
    public $id_namevariation;
    public $name;
    public $dopelniacz;
    public $celownik;
    public $biernik;
    public $narzednik;
    public $miejscownik;

    public static $definition = array(
        'table' => 'namevariations',
        'primary' => 'id_namevariation',
        'multilang' => false,
        'multilang_shop' => false,
        'fields' => array(
            'name' => array('type' => self::TYPE_STRING, 'lang' => false, 'validate' => 'isGenericName', 'size' => 512),
            'dopelniacz' => array('type' => self::TYPE_STRING, 'lang' => false, 'validate' => 'isGenericName', 'size' => 512),
            'celownik' => array('type' => self::TYPE_STRING, 'lang' => false, 'validate' => 'isGenericName', 'size' => 512),
            'biernik' => array('type' => self::TYPE_STRING, 'lang' => false, 'validate' => 'isGenericName', 'size' => 512),
            'narzednik' => array('type' => self::TYPE_STRING, 'lang' => false, 'validate' => 'isGenericName', 'size' => 512),
            'miejscownik' => array('type' => self::TYPE_STRING, 'lang' => false, 'validate' => 'isGenericName', 'size' => 512),
        ),
    );

    
    public static function getByName($name){
        $result = Db::getInstance()->getRow("
            SELECT * FROM `" . _DB_PREFIX_ . "namevariations` WHERE `name` = '" . $name . "'"
        );

        return $result;
    }
}

W tym miejscu przygotowujemy klasę, na podstawie której będą umieszczane wartości w bazie danych. Wewnątrz zmiennej $definition ustalamy również rodzaje pól w bazie danych.

Przygotowana została również funkcja getByName za pomocą, której pobierana jest wartość z bazy danych dla danego imienia, jeśli brak zwracana jest wartość false

Przygotujmy następnie plik controllers/admin/NameVariations.php i umieśćmy tam treść:

<?php

include_once(_PS_MODULE_DIR_.'namevariations/classes/NameVariation.php');

class NameVariationsController extends AdminController
{
    protected $position_identifier = 'id_namevariation';

    public function __construct()
    {
        $this->bootstrap = true;
        $this->table = 'namevariations';
        $this->list_id = 'namevariations';
        $this->identifier = 'id_namevariation';
        $this->className = 'NameVariation';
        $this->lang = false;
        $this->_defaultOrderBy = 'id_namevariation';

        parent::__construct();

        $this->fields_list = array(
            'id_namevariation' => array(
                'title' => $this->trans('ID', array(), 'Admin.Global'),
                'align' => 'center',
                'class' => 'fixed-width-xs',
            ),
            'name' => array(
                'title' => $this->trans('Name', array(), 'Admin.Global'),
                'align' => 'left',
            ),
            'dopelniacz' => array(
                'title' => $this->trans('Dopełniacz', array(), 'Admin.Global'),
                'align' => 'left',
            ),
            'celownik' => array(
                'title' => $this->trans('Celownik', array(), 'Admin.Global'),
                'align' => 'left',
            ),
            'biernik' => array(
                'title' => $this->trans('Biernik', array(), 'Admin.Global'),
                'align' => 'left',
            ),
            'narzednik' => array(
                'title' => $this->trans('Narzędnik', array(), 'Admin.Global'),
                'align' => 'left',
            ),
            'miejscownik' => array(
                'title' => $this->trans('Miejscownik', array(), 'Admin.Global'),
                'align' => 'left',
            ),
        );

        $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'),
            ),
        );
    }

Importujemy w tym miejscu plik naszej klasy i przygotowujemy funkcję __construct w której umieszczamy całą konfigurację tabeli wyświetlanej na stronie modułu, oraz podajemy informację z jakiej tabeli bazy danych mają być pobierane informacje.

Niżej przygotujmy kolejną funkcję:

    {
        if (empty($this->display)) {
            $this->page_header_toolbar_btn['new_name_variation'] = array(
                'href' => $this->context->link->getAdminLink('namevariations', true, array(), array(' addnamevariations' => 1)),
                'desc' => 'Dodaj nowe imię',
                'icon' => 'process-icon-new',
            );
        }

        parent::initPageHeaderToolbar();
    }

Za pomocą tej funkcji dodajemy do górnego paska menu przycisk przenoszący do formularza dodawania nowej wartości do bazy danych.

Do przygotowania została jeszcze jedna funkcja:

public function renderForm(){
        $this->fields_form = array(
            'tinymce' => true,
            'legend' => array(
                'title' => $this->l('Dodawanie imienia'),
                'icon' => 'icon-folder-close',
            ),
            'input' => array(
                array(
                    'type' => 'text',
                    'label' => $this->trans('Imię - Mianownik', array(), 'Admin.Global'),
                    'name' => 'name',
                    'id' => 'name',
                    'lang' => false,
                    'required' => true,
                ),
                array(
                    'type' => 'text',
                    'label' => $this->trans('Dopełniacz (kogo? nie ma)', array(), 'Admin.Global'),
                    'name' => 'dopelniacz',
                    'id' => 'dopelniacz',
                    'lang' => false,
                    'required' => false,
                ),
                array(
                    'type' => 'text',
                    'label' => $this->trans('Celownik (komu?)', array(), 'Admin.Global'),
                    'name' => 'celownik',
                    'id' => 'celownik',
                    'lang' => false,
                    'required' => false,
                ),
                array(
                    'type' => 'text',
                    'label' => $this->trans('Biernik (kogo? widzę)', array(), 'Admin.Global'),
                    'name' => 'biernik',
                    'id' => 'biernik',
                    'lang' => false,
                    'required' => false,
                ),
                array(
                    'type' => 'text',
                    'label' => $this->trans('Narzędnik (z kim?)', array(), 'Admin.Global'),
                    'name' => 'narzednik',
                    'id' => 'narzednik',
                    'lang' => false,
                    'required' => false,
                ),
                array(
                    'type' => 'text',
                    'label' => $this->trans('Miejscownik (o kim?)', array(), 'Admin.Global'),
                    'name' => 'miejscownik',
                    'id' => 'miejscownik',
                    'lang' => false,
                    'required' => false,
                ),
            ),
            'submit' => array(
                'title' => $this->trans('Save', array(), 'Admin.Actions'),
            ),
        );

        return parent::renderForm();
    }

Za pomocą tej funkcji przygotowujemy pola formularza, działa identycznie jak funkcja renderForm z podstawowego pliku dla modułu.

Po zainstalowaniu modułu będziemy widzieć po lewej stronie dodatkową pozycję w menu przenoszącą na stronę naszego modułu, gdzie możemy konfigurować pola.

Możemy dodawać warianty imion do bazy danych, ale nie będą one wyświetlane w mailach. Należy przygotować w tym celu klasy i kontrolery, które nadpiszą domyślne pliki Prestashop.

W tym celu tworzymy wewnątrz folderu naszego modułu folder override.

Wiele plików posiada funkcję wysyłające maile, dla każdego z nich musimy dodać pobieranie wariantów i przypisanie zmiennych.

Dla przykładu jest to plik override/classes/Customer.php jego wygląd powinien być następujący:

<?php
include_once(_PS_MODULE_DIR_.'namevariations/classes/NameVariation.php');

class Customer extends CustomerCore {

    public function transformToCustomer($idLang, $password = null)
    {
        if (!$this->isGuest()) {
            return false;
        }
        if (empty($password)) {
            $password = Tools::passwdGen(8, 'RANDOM');
        }
        if (!Validate::isPasswd($password)) {
            return false;
        }

        $language = new Language($idLang);
        if (!Validate::isLoadedObject($language)) {
            $language = Context::getContext()->language;
        }

        /** @var \PrestaShop\PrestaShop\Core\Crypto\Hashing $crypto */
        $crypto = ServiceLocator::get('\\PrestaShop\\PrestaShop\\Core\\Crypto\\Hashing');
        $this->is_guest = 0;
        $this->passwd = $crypto->hash($password);
        $this->cleanGroups();
        $this->addGroups(array(Configuration::get('PS_CUSTOMER_GROUP')));
        $this->id_default_group = Configuration::get('PS_CUSTOMER_GROUP');
        if ($this->update()) {
            $namevariation = NameVariation::getByName($this->firstname);
            
            if($namevariation === false){
                $namevariation['dopelniacz'] = $this->firstname;
                $namevariation['celownik'] = $this->firstname;
                $namevariation['biernik'] = $this->firstname;
                $namevariation['narzednik'] = $this->firstname;
                $namevariation['miejscownik'] = $this->firstname;
            }

            $vars = array(
                '{firstname}' => $this->firstname,
                '{lastname}' => $this->lastname,
                '{email}' => $this->email,
                '{dopelniacz}' => $namevariation['dopelniacz'],
                '{celownik}' => $namevariation['celownik'],
                '{biernik}' => $namevariation['biernik'],
                '{narzednik}' => $namevariation['narzednik'],
                '{miejscownik}' => $namevariation['miejscownik'],
            );
            Mail::Send(
                (int) $idLang,
                'guest_to_customer',
                Context::getContext()->getTranslator()->trans(
                    'Your guest account has been transformed into a customer account',
                    array(),
                    'Emails.Subject',
                    $language->locale
                ),
                $vars,
                $this->email,
                $this->firstname . ' ' . $this->lastname,
                null,
                null,
                null,
                null,
                _PS_MAIL_DIR_,
                false,
                (int) $this->id_shop
            );

            return true;
        }

        return false;
    }
}

Na górze załączamy plik klasy naszego modułu, następnie klasa jest tworzona na zasadzie class Klasa extends KlasaCore {}

W przypadku klasy Customer wysyłka maila pojawia się wewnątrz funkcji transformToCustomer, przed zmienną $vars dodajemy następującą treść:

$namevariation = NameVariation::getByName($this->firstname);
            
if($namevariation === false){
   $namevariation['dopelniacz'] = $this->firstname;
   $namevariation['celownik'] = $this->firstname;
   $namevariation['biernik'] = $this->firstname;
   $namevariation['narzednik'] = $this->firstname;
   $namevariation['miejscownik'] = $this->firstname;
}

Następnie do tablicy $vars dodajemy treść:

'{dopelniacz}' => $namevariation['dopelniacz'],
'{celownik}' => $namevariation['celownik'],
'{biernik}' => $namevariation['biernik'],
'{narzednik}' => $namevariation['narzednik'],
'{miejscownik}' => $namevariation['miejscownik'],

W przypadku różnych plików imię jest przekazywane w inny sposób, w tym przypadku jest to $this->firstname

Wszystkie pliki i ich funkcje jakie należy podmienić to:

  • classes/Customer.php
    • transformToCustomer()
  • classes/PaymentModule.php
    • validateOrder()
    • createOrderCartRules()
  • classes/form/CustomerPersister.php
    • sendConfirmationMail()
  • classes/order/OrderCarrier.php
    • sendInTransitEmail()
  • classes/order/OrderHistory.php
    • sendEmail()
    • changeIdOrderState()
  • controllers/admin/AdminCustomerThreadsController.php
    • postProcess()
  • controllers/admin/AdminImportController.php
    • ajaxProcessImport()
  • controllers/admin/AdminLoginController.php
    • processForgot()
    • processReset()
  • controllers/admin/AdminOrdersController.php
    • postProcess()
    • ajaxProcessSendMailValidateOrder()
  • controllers/admin/AdminReturnController.php
    • postProcess()
  • controllers/front/ OrderDetailController.php
    • postProcess()
  • controllers/front/PasswordController.php
    • sendRenewPasswordLink()
    • changePassword()

Aby skonfigurować szablon maila należy z menu bocznego wybrać zakładkę Międzynarodowy->Tłumaczenia

W tym miejscu wybieramy następujące wartości:

  1. Tłumaczenie e-maila
  2. Treść
  3. Nazwa naszego szablonu
  4. Język, dla którego chcemy tłumaczyć

Następnie klikamy przycisk „Modyfikuj” (5)

Rozwińmy listę szablonów, które chcemy zmodyfikować a następnie wybierzmy jakiś konkretny.

W tym miejscu możemy użyć naszych zmiennych, a zostanie to wykorzystane w naszych mailach.

Dla powyższego szablonu przy dodaniu imion do bazy zostaną wysłane np. następujące treści:

  • Krystianie
  • Kamilu
  • Marku
  • Michale

Itd.

Jeżeli w mailu w wiadomości będzie po prostu wysyłać się treść „{mianownik}”, „{celownik}” itd. to należy zmodyfikować główne pliki klas i kontrolerów Prestashop, ponieważ niestety czasem klasy override nie działają poprawnie, można też próbować zainstalować moduł ponownie.

Kategorie
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.

Kategorie
Poradniki

Tworzenie modułu Prestashop cz. 2 – własne tabele, rozbudowany moduł CRUD, własny widok w panelu

W tej części poradnika wykorzystamy pliki, które przygotowaliśmy w części pierwszej.

W pierwszej kolejności wprowadzimy modyfikacje w pliku myfirstmodule.php w funkcji install() wprowadźmy tam następującą treść:

 if (!parent::install()
     || !$this->registerHook('displayHome')
     || !$this->installDB()
    ) {
    return false;
}
return true;

Dodaliśmy do instrukcji warunkowej if kod: || !$this->installDB() jest to funkcja, którą zaraz utworzymy, odpowiedzialna za utworzenie naszej tabeli w bazie danych.

Stwórzmy teraz funkcje installDB(), która będzie zawierać po prostu zwrócone zapytanie SQL:

public function installDB(){
        $correct = true;

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

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

        return $correct;
    }

Funkcja ta utworzy nam tabelę ps_myfirstmodule, do której będziemy zapisywać ustawienia, oraz tabelę ps_myfirstmodule_lang, do której będziemy zapisywać treści pól, dla odpowiednich języków.

Zmieńmy teraz strukturę naszych folderów, przy bardziej rozbudowanych modułach należy to trochę uporządkować, utwórzmy więc folder views a w nim dwa pod foldery: front i admin

Przenieśmy do views/front nasz plik myfirstmodule.tpl

Zmieńmy teraz w funkcji hookDisplayHome() wartość return na:

return $this->display(__FILE__, 'views/front/myfirstmodule.tpl');

W folderze views/admin utwórzmy plik o nazwie list.tpl

Następnie w pliku myfirstmodule.php w funkcji getContent() zmodyfikujmy zawartość linijki z return na:

$this->context->smarty->assign(array(
            'link' => $this->context->link,
            'elements' => []
        ));

return $output.$this->displayForm().$this->display(__FILE__, 'views/admin/list.tpl');

Dodaliśmy do zwracanego widoku zmienną link, która pozwoli generować odnośniki do panelu administracyjnego, dodaliśmy również zmienną elements która na ten moment pozostaje pusta. Do zwracanego widoku dołączamy również plik szablonu list.tpl

W tym pliku będziemy wyświetlać listę elementów naszego modułu, przygotujmy więc ten widok:

<div class="panel"><h3><i class="icon-list-ul"></i> Lista elementów 
    <span class="panel-heading-action">
        <a id="desc-product-new" class="list-toolbar-btn" href="{$link->getAdminLink('AdminModules')}&amp;configure=myfirstmodule&amp;addElement=1">
            <span title="" data-toggle="tooltip" class="label-tooltip" data-original-title="{l s='Add new' d='Admin.Actions'}" data-html="true">
                <i class="process-icon-new "></i>
            </span>
        </a>
    </span>
    </h3>
    <div id="elementsContent">
        <div id="elements">
            {foreach from=$elements item=element}
                <div id="elements_{$element.id_myfirstmodule}" class="panel">
                    <div class="row">
                        <div class="col-md-10">
                        {foreach from=$element.lang item=elementLang}
                                <h4 class="pull-left" style="padding-left: 10px; padding-right: 10px;">
                                    <b>{$elementLang.code}</b><br />
                                    {$elementLang.name}<br />
                                    {$elementLang.url}
                                </h4>
                        {/foreach}
                        </div>
                        <div class="col-md-2 text-right">
                            <h4>
                                {($element.blank == 1) ? 'Nowa karta' : 'Ta sama karta'}
                            </h4>

                            <div class="btn-group-action pull-right">
                                <a class="btn btn-default"
                                    href="{$link->getAdminLink('AdminModules')}&amp;configure=myfirstmodule&amp;id_myfirstmodule={$element.id_myfirstmodule}">
                                    <i class="icon-edit"></i>
                                    {l s='Edit' d='Admin.Actions'}
                                </a>
                                <a class="btn btn-default"
                                    href="{$link->getAdminLink('AdminModules')}&amp;configure=myfirstmodule&amp;delete_id_myfirstmodule={$element.id_myfirstmodule}">
                                    <i class="icon-trash"></i>
                                    {l s='Delete' d='Admin.Actions'}
                                </a>
                            </div>
                        </div>
                    </div>
                </div>
            {/foreach}
        </div>
    </div>
</div>

Widok ten będzie wyświetlał prosty panel z listą dodanych elementów. Możemy zauważyć, że używana jest zmienna $link do generowania odnośnika dla panelu administracyjnego.

Wyświetlane będą wartości dla każdego języka.

Strona naszego modułu powinna wyglądać tak jak powyżej, przejdźmy teraz do dalszych modyfikacji pliku myfirstmodule.php

public function displayForm($mode = 0)
    {
        if($mode == 0){
            $fields_form[0]['form'] = array(
                'legend' => array(
                    'title' => $this->l('Mój pierwszy moduł'),
                ),
                'input' => array(
                    array(
                        'type' => 'text',
                        'label' => $this->l('Wartość modułu'),
                        'name' => 'myfirstmodule',
                        'lang' => false,
                        'size' => 20,
                        'required' => true
                    ),
                ),
                'submit' => array(
                    'title' => $this->l('Save'),
                    'class' => 'btn btn-default pull-right'
                )
            );
        } else if($mode == 1 || $mode == 2){
            $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')
                        )
                    )
                ),
            );

            if($mode == 1){
                $fields_form[0]['form'] = array(
                    'legend' => array(
                        'title' => $this->l('Dodaj element modułu'),
                    ),
                    'input' => $input,
                    'submit' => array(
                        'title' => $this->l('Save'),
                        'class' => 'btn btn-default pull-right'
                    ),
                    'buttons' => array(
                        array(
                            'href' => AdminController::$currentIndex.'&amp;configure='.$this->name.'&amp;token='.Tools::getAdminTokenLite('AdminModules'),
                            'title' => $this->l('Back to list'),
                            'icon' => 'process-icon-back'
                        )
                    )
                );
            }

if($mode == 2){
                $input[] = array(
                    'type' => 'hidden',
                    'name' => 'id_myfirstmodule',
                );

                $fields_form[0]['form'] = array(
                    'legend' => array(
                        'title' => $this->l('Edytuj element modułu'),
                    ),
                    'input' => $input,
                    'submit' => array(
                        'title' => $this->l('Save'),
                        'class' => 'btn btn-default pull-right'
                    ),
                    'buttons' => array(
                        array(
                            'href' => AdminController::$currentIndex.'&amp;configure='.$this->name.'&amp;token='.Tools::getAdminTokenLite('AdminModules'),
                            'title' => $this->l('Back to list'),
                            'icon' => 'process-icon-back'
                        )
                    )
                );
            }

W funkcji displayForm() dodaliśmy parametr $mode za pomocą, którego będziemy rozróżniać dla jakiej strony chcemy zwrócić formularz.

Następnie tablicę, która przechowuje umieszczamy wewnątrz instrukcji warunkowej. A wewnątrz kolejnego warunku umieszczamy pola formularza dla dodawania i edycji naszego elementu.

Przy formularzu dodawania/edycji można zauważyć nowy indeks buttons, w którym dodajemy przycisk pozwalający powrócić do listy w module.

Przygotowany od razu został wariant dla formularza edycji ($mode == 2). Dodajemy tam dodatkowe pole id_myfirstmodule, gdzie będziemy przechowywać informację o tym jaki element aktualnie edytujemy.

$helper->default_form_language = $this->context->language->id;

        if($mode == 0){
            $helper->submit_action = 'submit'.$this->name;

            $helper->tpl_vars = array(
                'fields_value' => array('myfirstmodule' => Configuration::get('myfirstmodule')),
                'languages' => $this->context->controller->getLanguages(),
            );
        } else if($mode == 1){
            $helper->submit_action = 'submit_newelement'.$this->name;

            $values = [];

            $helper->tpl_vars = array(
                'fields_value' => $values,
                'languages' => $this->context->controller->getLanguages(),
            );
        }

Również funkcję dotyczącą wartości pól formularza i nazwy metody zapisu umieszczamy wewnątrz instrukcji warunkowej.

Dodajemy również ustawienia przycisków dla dodawania nowego elementu.

Nad instrukcją warunkową możemy jeszcze zobaczyć linijkę ustawiającą domyślny język dla formularzy, dzięki czemu wielojęzyczne pola będą wyświetlać się poprawnie.

Wewnątrz funkcji getContent() dopiszmy do instrukcji warunkowej następującą treść:

else if(Tools::isSubmit('addElement')){
     return $output.$this->displayForm(1);
}

Teraz będzie poprawnie wyświetlać się formularz dodawania elementu do modułu.

Poniżej dodajmy kolejną instrukcję dotyczącą zapisu zmian.

 else if(Tools::isSubmit('submit_newelement'.$this->name)){
            $languages = Language::getLanguages(false, $this->context->shop->id);
            $id_shop = (int)Context::getContext()->shop->id;

            $values = array(
                'id_shop' => $id_shop,
                'blank' => (int)Tools::getValue('blank')
            );

            Db::getInstance()->insert('myfirstmodule', $values);
            $id_myfirstmodule = (int)Db::getInstance()->Insert_ID();

            foreach($languages as $language){
                $id_lang = $language['id_lang'];

                $values = array(
                    'id_myfirstmodule' => $id_myfirstmodule,
                    'id_lang' => $id_lang,
                    'name' => strval(Tools::getValue('name_' . $id_lang)),
                    'url' => strval(Tools::getValue('url_' . $id_lang)),
                );

                Db::getInstance()->insert('myfirstmodule_lang', $values);
            }

            $output .= $this->displayConfirmation($this->l('Dodano element.'));

}

Tłumacząc, ta instrukcja uruchomi się w momencie, kiedy zapisujemy element. Na początku dodajemy element do tabeli ps_myfirstmodule, pobieramy ID dodanego elementu, a następie pola na podstawie dostępnych języków do tabeli ps_myfirstmodule_lang

Po wszystkim wyświetlamy informację, że dodano element.

Elementy będą dodawać się do bazy danych, ale niestety nie będą wyświetlać się jeszcze na naszej liście.

Na samym dole funkcji getContent() zmieniamy indeks dla tablicy w funkcji assign na:

'elements' => $this->getElements()

Następnie tworzymy funkcję getElements(), w której będziemy pobierać elementy.

public function getElements(){
        $id_shop = (int)Context::getContext()->shop->id;
        $languages = Language::getLanguages(false, $this->context->shop->id);

        $elementsArray = [];

        $elements = Db::getInstance()->ExecuteS("
            SELECT * FROM `" . _DB_PREFIX_ . "myfirstmodule` WHERE `id_shop` = " . $id_shop
        );

        foreach($elements as $element){
            foreach($languages as $language){
                $elementLang = Db::getInstance()->ExecuteS("
                    SELECT * FROM `" . _DB_PREFIX_ . "myfirstmodule_lang` WHERE `id_myfirstmodule` = " . $element['id_myfirstmodule'] . ' AND `id_lang` = ' . $language['id_lang']
                )[0];

                $element['lang'][$language['id_lang']] = array(
                    'code' => $language['iso_code'],
                    'name' => $elementLang['name'],
                    'url' => $elementLang['url'],
                );
            }

            $elementsArray[] = $element;
        }

        return $elementsArray;
    }

Funkcja przygotowuje tabele do wyświetlania elementów na liście, z rozdzieleniem wartości na języki.

Pobieramy tutaj najpierw podstawowe ustawienia a następie wartości dla każdego języka dostępnego w sklepie.

Lista powinna wyświetlać się w podobny sposób po dodaniu elementów do modułu.

Przygotujmy teraz formularz dla edycji elementu.

Wewnątrz funkcji displayForm(), na samym dole dodajmy wariant dla instrukcji warunkowej.

 else if($mode == 2){
            $helper->submit_action = 'submit_updateelement'.$this->name;

            $values = [];

            $helper->tpl_vars = array(
                'fields_value' => $this->getValues(),
                'languages' => $this->context->controller->getLanguages(),
            );
        }

Zmienia się tutaj nazwa akcji zapisanie, oraz pola formularza pobierane są z funkcji getValues(), którą teraz przygotujemy.

        $id_myfirstmodule = (int)Tools::getValue('id_myfirstmodule');
        $values = array();

        $element = Db::getInstance()->ExecuteS("
            SELECT * FROM `" . _DB_PREFIX_ . "myfirstmodule` WHERE `id_myfirstmodule` = " . $id_myfirstmodule
        )[0];

        $values['id_myfirstmodule'] = $id_myfirstmodule;
        $values['blank'] = $element['blank'];

        $elementLangs = Db::getInstance()->ExecuteS("
            SELECT * FROM `" . _DB_PREFIX_ . "myfirstmodule_lang` WHERE `id_myfirstmodule` = " . $id_myfirstmodule
        );

        foreach($elementLangs as $elementLang){
            $values['name'][$elementLang['id_lang']] = $elementLang['name'];
            $values['url'][$elementLang['id_lang']] = $elementLang['url'];
        }

        return $values;
    }

Wartość ID elementu pobieramy z adresu a następie pobieramy element z bazy danych oraz tekst dla każdego wariantu językowego elementu.

Tablica dla pól wielojęzycznych musi być ułożona w następujący sposób:

'nazwa_pola' => array(
        'id_jezyka' => 'wartosc',
        'id_jezyka_2' => 'wartosc_2'
    )
)

W przypadku, gdy pole nie jest wielojęzyczne piszemy po prostu ‘nazwa_pola’ => ‘wartosc’

Następnie w funkcji getContent() dopisujemy wariant zwracania pola formularza do edycji:

else if(Tools::isSubmit('id_myfirstmodule')){
   return $output.$this->displayForm(2);
}

Jeśli wszystko jest w porządku, formularz będzie wyświetlał się tak jak powyżej.

Pozostaje nam przygotować funkcję aktualizującą element w bazie danych. Dodajemy ją naszych instrukcji warunkowych w funkcji getContent(), lecz należy dodać ten warunek pod pierwszym z warunków, a nie na samym dole.

  else if(Tools::isSubmit('submit_updateelement'.$this->name)){
            $languages = Language::getLanguages(false, $this->context->shop->id);

            $id_myfirstmodule =  (int)Tools::getValue('id_myfirstmodule');

            $values = array(
                'blank' => pSQL((int)Tools::getValue('blank'))
            );

            Db::getInstance()->update('myfirstmodule', $values, 'id_myfirstmodule = ' . $id_myfirstmodule);

            foreach($languages as $language){
                $id_lang = $language['id_lang'];

                $values = array(
                    'name' => pSQL(strval(Tools::getValue('name_' . $id_lang))),
                    'url' => pSQL(strval(Tools::getValue('url_' . $id_lang))),
                );

                Db::getInstance()->update('myfirstmodule_lang', $values, 'id_myfirstmodule = ' . $id_myfirstmodule . ' AND id_lang = ' . $id_lang);
            }

            $output .= $this->displayConfirmation($this->l('Zaktualizowano element.'));
        }

Funkcja aktualizuje kolumnę blank w głównej tabeli na podstawie ID

Następnie aktualizuje pola name i url dla tabeli myfirstmodule_lang na podstawie języków dostępnych w sklepie. Używana jest funkcja pSQL na wartościach, ponieważ jest to funkcja zabezpieczająca przed wstrzykiwaniem SQL.

 Jeśli wszystko w porządku zapisane wartości powinny się aktualizować a my zostaniemy przeniesieni na główną stronę modułu.

Pozostało nam przygotować funkcję usuwającą elementy z bazy danych.

W tym celu dodajemy następującą treść do funkcji getContent()

else if(Tools::isSubmit('delete_id_myfirstmodule')){
            $id_myfirstmodule = (int)Tools::getValue('delete_id_myfirstmodule');

            Db::getInstance()->delete('myfirstmodule', 'id_myfirstmodule = ' . $id_myfirstmodule);
            Db::getInstance()->delete('myfirstmodule_lang', 'id_myfirstmodule = ' . $id_myfirstmodule);

            $output .= $this->displayConfirmation($this->l('Usunięto element.'));
       }

Pobieramy ID elementu z adresu, a następie usuwane są wartości z dwóch tabel naszego modułu.

Dodajmy kilka wartości do modułu i poprawmy na koniec szablon wyświetlania modułu na sklepie.

Zmieńmy na początek zawartość funkcji hookDisplayHome()

public function hookDisplayHome($params)
    {
        $id_shop = (int)Context::getContext()->shop->id;
        $id_lang = (int)Context::getContext()->language->id;

        $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
        );

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

Funkcja ta teraz pobiera z bazy danych nasze elementy do zmiennej elements a następie zwraca do szablonu.

Edytujmy więc plik views/front/myfirstmodule.tpl na następującą treść:

<h3>{$myfirstmodule}</h3>

{if count($elements) > 0}
    <ul>
        {foreach from=$elements item=element}
            <li><a href="{$element.url}" {if $element.blank == 1} target="_blank" {/if}>{$element.name}</a></li>
        {/foreach}
    </ul>
{/if}

Wyświetlimy nasze elementy na liście w powyższy sposób. Dostęp do każdego parametru mamy poprzez kropkę

Kategorie
Poradniki

Tworzenie modułu Prestashop cz. 1 – struktura katalogów, pierwszy prosty moduł

W pierwszej kolejności tworzymy folder naszego modułu, nazwa powinna nie zawierać spacji, być złożona z małych liter i bez polskich znaków.

Nazwa nie powinna też być zbyt długa, na potrzeby tego poradnika stwórzmy folder o nazwie: myfirstmodule

Wewnątrz utworzonego folderu tworzymy plik PHP o tej samej nazwie, czyli: myfirstmodule.php

Oprócz tego pliku tworzymy jeszcze dwa inne pliki: config.xml i myfirstmodule.tpl

Te trzy pliki tworzą podstawową budową strukturę modułu.

Otwórzmy w pierwszej kolejności plik config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<module>
    <name>myfirstmodule</name>
    <displayName><![CDATA[Mój pierwszy moduł]]></displayName>
    <version><![CDATA[1.0.0]]></version>
    <description><![CDATA[Opis mojego pierwszego modułu.]]></description>
    <author><![CDATA[Akademia sklepów]]></author>
    <is_configurable>1</is_configurable>
    <need_instance>0</need_instance>
    <limited_countries></limited_countries>
</module>

Jest to podstawowy plik konfiguracyjny naszego modułu, pozwala on przyśpieszyć jego wczytywanie.

Uzupełniamy plik powyższą treścią, widoczne powyżej parametry to:

  • name – nazwa modułu (taka sama jak folder)
  • displayName – wyświetlana nazwa modułu, widoczna w Panelu Administracyjnym
  • version – wersja modułu
  • description – opis modułu widoczny w Panelu Administracyjnym.
  • author – wyświetlany autor modułu.
  • is_configurable – informacja czy moduł posiada stronę konfiguracji go, czy nie.
  • need_instance – informuje czy moduł ma być wywoływany w momencie wyświetlania go na liście modułów, przydatne w niektórych przypadkach.
  • limited_countries – kody krajów, dla których moduł ma być ograniczony. Kody muszą być dwuznakowe (np. pl, fr, de) i oddzielone przecinkami.

W następnej kolejności otwórzmy plik myfirstmodule.php  i uzupełniamy go poniższą treścią

<?php

if (!defined('_PS_VERSION_'))
    exit();

class myFirstModule extends Module
{
    public function __construct()
    {
        $this->name = 'myfirstmodule';
        $this->version = '1.0.0';
        $this->author = 'Akademia Sklepów';
        $this->need_instance = 0;
        $this->ps_versions_compliancy = array('min' => '1.7.1.0', 'max' => _PS_VERSION_);
        $this->bootstrap = true;

        parent::__construct();

        $this->displayName = $this->l('Mój pierwszy moduł', 'myfirstmodule');
        $this->description = $this->l('Opis mojego pierwszego modułu.', 'myfirstmodule');
    }
}

W tym miejscu również ustalamy parametry naszego modułu, tak jak w pliku config.xml

Pojawiają się tutaj jednak dwa nowe pola:

  • ps_versions_compliancy – tablica z informacją dla jakich wersji Prestashop przeznaczony jest moduł, wartość _PS_VERSION_ użyta przy max oznacza wersję aktualnie używanego sklepu Prestashop
  • bootstrap – informacja o tym czy moduł korzysta z styli css bootstrap

Niżej tworzymy w naszym module dwie kolejne funkcje:

public function install()
    {
        if (!parent::install() 
        || !$this->registerHook('displayHome')
        ) {
            return false;
        }
        return true;
    } 

public function uninstall()
    {
        return parent::uninstall();
    }

Funkcje te są wywoływane odpowiednio podczas instalowana i odinstalowywania nasze modułu. Wywołanie parent::install() i parent::uninstall() uruchamia domyślne funkcje dla modułów.

W funkcji instalacji wywoływana jest instrukcja warunkowa, która w przypadku niepowodzenia któregoś kroku instalacji informuje, że ta nie udała się.

Wywołanie $this->registerHook('displayHome') przypisuje nasz moduł do strony głównej, możemy przypisać nasz moduł do różnych widoków. Pełna lista dostępna jest TUTAJ.

Przejdźmy teraz do wyświetlania formularza naszego modułu. Utwórzmy na początek poniższą funkcję.

public function displayForm()
    {
        $fields_form[0]['form'] = array(
            'legend' => array(
                'title' => $this->l('Mój pierwszy moduł'),
            ),
            'input' => array(
                array(
                    'type' => 'text',
                    'label' => $this->l('Wartość modułu'),
                    'name' => 'myfirstmodule',
                    'lang' => false,
                    'size' => 20,
                    'required' => true
                ),
            ),
            'submit' => array(
                'title' => $this->l('Save'),
                'class' => 'btn btn-default pull-right'
            )
        );

        $helper = new HelperForm();

        $helper->module = $this;
        $helper->name_controller = $this->name;
        $helper->token = Tools::getAdminTokenLite('AdminModules');
        $helper->currentIndex = AdminController::$currentIndex.'&amp;configure='.$this->name;

        $helper->title = $this->displayName;
        $helper->show_toolbar = true;      
        $helper->toolbar_scroll = true;    
        $helper->submit_action = 'submit'.$this->name;
        $helper->toolbar_btn = array(
            'save' =>
                array(
                    'desc' => $this->l('Save'),
                    'href' => AdminController::$currentIndex.'&amp;configure='.$this->name.'&amp;save'.$this->name.
                        '&amp;token='.Tools::getAdminTokenLite('AdminModules'),
                ),
            'back' => array(
                'href' => AdminController::$currentIndex.'&amp;token='.Tools::getAdminTokenLite('AdminModules'),
                'desc' => $this->l('Back to list')
            )
        );

        $helper->tpl_vars = array(
            'fields_value' => array('myfirstmodule' => Configuration::get('myfirstmodule')),
            'languages' => $this->context->controller->getLanguages(),
        );

        return $helper->generateForm($fields_form);
    }

Jest to funkcja przygotowująca formularz naszego modułu, pod uwagę należy wziąć głównie tablicę fields_form gdzie przekazujemy jakie pola chcemy umieścić na stronie konfiguracji modułu.

Pod indeksem legend ustalamy tytuł bloku formularza,

Indeks input zawiera tablicę z wszystkimi polami,

Pod indeksem submit ustalamy przycisk zapisu formularza.

Tworząc klasę HelperForm możemy ustalić następujące jej parametry:

  • module – moduł którego dotyczy formularz
  • name_controller – nazwa kontrolera wyświetlana w liście aktywnych zakładek
  • token – pobieramy go bez zmian za pomocą funkcji Tools::getAdminTokenLite(‘AdminModules’);
  • currentIndex – aktualny adres w momencie wyświetlania formularza
  • title – nazwa wyświetlana a stronie zarządzania modułem
  • show_toolbar – możemy wyłączyć wyświetlanie górnego paska
  • toolbar_scroll – jeśli ustawimy true, górny pasek będzie przyklejony podczas przewijania strony
  • submit_action – nazwa akcji podczas zapisywania formularza
  • toolbar_btn – przyciski na górnym pasku w module, możemy zmieniać adres przycisku Wróć, przydatne przy bardziej rozbudowanych modułach
  • tpl_vars – tablica wartościami dla widoku, indeks fields_value zawiera wartości poszczególnych pól formularza, a indeks languages dostępne języki w sklepie, wymagane, gdy pole formularza ma ustawiony indeks lang na true. Wtedy też musimy podać wartość pola dla każdego z języków.

Następnie tworzymy kolejną funkcję, aby wyświetlać nasz formularz:

public function getContent()
    {
        $output = null;

       if (Tools::isSubmit('submit'.$this->name)) {
            $myfirstmodule = strval(Tools::getValue('myfirstmodule'));

            if (!isset($myfirstmodule)){
                $output .= $this->displayError($this->l('Proszę uzupełnić pole.'));
            } else {
                Configuration::updateValue('myfirstmodule', $myfirstmodule);

                $output .= $this->displayConfirmation($this->l('Ustawienia zostały zaktualizowane.'));
            }
       }

        return $output.$this->displayForm();
    }

Funkcja ta po prostu zwraca do widoku formularz, jeśli jednak aktualnie jest wysyłany formularz naszego modułu, pobierana jest wartość pola myfirstmodule z formularza i aktualizowana jest wartość w konfiguracji sklepu, jeśli pole jest puste wyświetli się odpowiednia informacja.

Po zapisaniu, znów wyświetli się formularz z informacja o zapisanych zmianach.

Ostatnia funkcja jaką musimy przygotować to:

public function hookDisplayHome($params)
    {
        $this->context->smarty->assign(
            array('myfirstmodule' => Configuration::get('myfirstmodule'))
        );
        return $this->display(__FILE__, 'myfirstmodule.tpl');
    }

Funkcja ta jest uruchamiana na stronie głównej w celu wyświetlenia odpowiedniej zawartości.

Funkcja assign przypisuje zmienne do naszego pliku szablonu. Nazwa indeksu jest nazwą zmiennej.

Następnie zwracamy za pomocą funkcji display() plik szablonu, który chcemy wyświetlać w ramach modułu.

Otwórzmy teraz nasz plik szablonu myfirstmodule.tpl

Uzupełnijmy go prostą treścią:

<h3>{$myfirstmodule}</h3>

Ostatnie co nam zostało to utworzenie pliku zip z folderem naszego modułu (np. programem WinRar lub prawy przycisk myszy -> Wyślij do -> Folder skompresowany (zip))  

Uruchomienie modułu

W naszym sklepie przejdźmy do zakładki Moduły -> Module Manager

Klikamy przycisk „Załaduj moduł” a następnie wybierzmy nasz plik .zip

Kliknijmy przycisk „Konfiguruj” w celu zarządzania naszym modułem.

Uzupełnijmy wartość naszego modułu i kliknijmy przycisk „Zapisz”.

Odwiedzając nasz sklep, możemy zobaczyć na samym dole wyświetlany nasz moduł. Jest na samym dole, ponieważ każdy moduł domyślnie dodawany jest na sam dół danego zaczepu.

Możemy zmienić to przechodząc w panelu do zakładki Wygląd -> Pozycje

Wyszukujemy w tym miejscu naszego modułu, a następnie przesuwamy jego pozycję za pomocą strzałki w górę, widocznej obok modułu.

Kategorie
Poradniki

Zarządzanie blokami linków w Prestashop 1.7

Aby przejść do zarządzania blokami linków z menu bocznego należy wybrać pozycję „Moduły” (1), a następnie „Module Manager” (2).

Następnie należy w wyszukiwarce (1) w pisać „Lista linków”, a następnie kliknąć przycisk lupy (2), lub klawisz Enter.

Na liście pojawią się znalezione moduły, przy module „Lista linków” klikamy przycisk „Konfiguruj” (3).

Na liście domyślnie znajdują się już jakieś bloki linków, możemy edytować jeden z nich klikając przycisk „pióra” w odpowiednim wierszu.

Edytując/Tworząc blok możemy znaleźć następujące pola formularza:

  1. Nazwa naszego bloku wyświetlana też na jego górze.
  2. Pozycja bloku w sklepie (każdy szablon ma zdefiniowane takie pozycje)
  3. Linki do stron CMS które mają być umieszczone
  4. Linki do zdefiniowanych stron produktów
  5. Linki do stron statycznych takie jak np. Kontakt
    Wybierając linki po prostu zaznaczamy je, a będą wyświetlane na liście
  6. Niestandardowe linki, gdzie podajemy po prostu nazwę odnośnika i jego adres

Kolejne niestandardowe linki możemy dodawać przyciskiem „Dodaj” (7). Każdy taki dodany link, ma pod sobą przycisk „Usuń” (8), za pomocą, którego można usunąć dany wiersz.

Aby zapisać wszystkie zmiany, należy nacisnąć przycisk „Zapisz” (9).

Możemy również dodać nowy blok, klikając przycisk „Nowy blok” w prawym górnym rogu.

Podczas tworzenia nowego bloku, mamy do czynienia z tym samym formularzem.

Rozwijając listę „Przypnij do” znajdziemy różne bloku, dla przykładu „displayTop” wyświetli nasz blok na samej górze strony.

Lista bloków jest duża, niektóre są tylko na stronie produktów, w koszyku, czy też w Panelu Administracyjnym. W większości szablonów dostępne bloki powtarzają się, ale można znaleźć pewne różnice.

Kategorie
Poradniki

Multisklep przy użyciu Prestashop

W pierwszej kolejności musimy włączyć tą funkcjonalność w naszym sklepie.

W tym celu z menu bocznego należy wybrać „Preferencje” (1), a następnie pozycje „Ogólny” (2).

Wśród opcji odnajdujemy pozycję „Włącz Multisklep” (1) i ustawiamy ją na „Tak”.

Następnie zapisujemy ustawienia klikając przycisk „Zapisz” (2) na dole strony.

Następnie z menu bocznego wybierzmy element „Zaawansowane” (1) a następnie pozycję „Multisklep” (2)

W tym miejscu będziemy mogli skonfigurować nasze sklepy.

Po przejściu do strony ustawień Multisklepów możemy dodać kolejny sklep klikając przycisk „Dodaj nowy sklep” w prawy górnym rogu strony.

Podczas dodawania kolejnego sklepu znajdziemy następujące pola formularza:

  1. Nazwa nowego sklepu
  2. Grupa sklepów do której ma przynależeć. Gdy posiadamy dużo sklepów podłączonych do Multisklepu możemy je grupować w grupy co pozwoli czytelniej nimi zarządzać.
  3. Główna kategoria w sklepie
  4. Kategorie, które mają być w tym sklepie dostępne
  5. Domyślny szablon, dostępne w tym miejscu są wszystkie zainstalowane
  6. Jeśli zaznaczymy tą opcje będziemy mogli zaimportować do nowego sklepu dane z innego, odblokowuje to poniższe opcje.
  7. Sklep, z którego chcemy importować dane do nowego
  8. W tym miejscu zaznaczamy, które dane chcemy importować, mamy tutaj dostęp do wielu danych.

Po wypełnieniu powyższych pól formularza możemy zapisać ustawienia, klikając przycisk „Zapisz” (9) na dole strony.

Po zapisaniu zmian sklep pojawi się na liście, aby ustalić jego adres należy nacisnąć odnośnik „Kliknij tutaj aby określić URL dla tego sklepu”.

Znajdziemy w tym miejscu następujące pola formularza:

  1. W tym miejscu wybieramy dla którego sklepu adres ustalamy
  2. W tym miejscu zaznaczamy czy dodawany adres jest adresem głównym dla sklepu, każdy sklep może mieć kilka adresów, każdy adres przekierowuje do jednego głównego. Gdy dodajemy pierwszy adres, automatycznie jest dodawany jako główny.
  3. W tym miejscu włączamy dany adres.
  4. Adres dodanego sklepu
  5. Adres dodanego sklepu z obsługą SSL
  6. Treść dodawana na końcu adresu sklepu
  7. Może zadecydować, żeby sklep posiadał adres drugisklep.akademisklepow.pl/zabawki
    Inny sklep za to może mieć końcówkę /ubrania
  8. Informacja o tym jak ostatecznie będzie wyglądał adres.

Po ustawieniu wszystkich powyższych danych możemy zapisać ustawienia klikając przycisk w prawym dolnym rogu „Zapisz” (10).

Wszystkie treści można edytować pod kątem jednego sklepu, dla przykładu edytując produkt możemy wybrać dla którego sklepu chcemy go edytować.

W tym celu należy rozwinąć listę w prawym górnym rogu (1) i wybrać sklep, dla którego widok produktu chcemy otworzyć (2).

Dla drugiego sklepu możemy uzupełnić całkowicie inne dane, w tym również stany magazynowe czy ceny.

Ponadto dla każdego sklepu możemy wprowadzić zupełnie nowe produkty, które nie są dostępne na innym, tak samo wygląda to w przypadku Kategorii czy Atrybutów.

Przełączając się w prawym górnym rogu pomiędzy sklepami zarządzamy nimi zupełnie osobno bez wpływu na drugi sklep. Również motywy czy położenie i treść modułów są niezależne pomiędzy sklepami.

Kategorie
Poradniki

Wielojęzyczny sklep Prestashop

Prestashop posiada wbudowaną funkcjonalność obsługi wielu języków sklepu.

Aby zarządzać językami naszego sklepu należy z menu bocznego wybrać zakładkę „Międzynarodowy” (1), a następnie pozycję „Lokalizacja” (2).

Przechodzimy do zakładki „Języki” (1), a następnie klikamy w przycisk „Dodaj nowy język” (2), przejdziemy do formularza dodawania języków.

W formularzu możemy znaleźć następujące pola:

  1. Nazwa języka, który dodajemy
  2. Dwu znakowy kod języka (np. pl, en, de, fr, it)
  3. Pięcioznakowy kod języka (np. pl-PL, en-US, en-UK, de-DE)
  4. Format daty dla danego języka, dostępne są następujące symbole:
    • Y – rok
    • m – miesiąc
    • d – dzień
  5. Format pełnej daty języka, dostępne są następujące symbole:
    • Y – rok
    • m – miesiąc
    • d – dzień
    • H – godzina
    • i – minuta
    • s – sekundy
  6. Flaga dodawanego języka
  7. Obraz wyświetlany, gdy nie jest dostępny obraz języka
  8. Jeśli zaznaczymy tą opcję treść i szablon będą przygotowane pod wyświetlanie od prawej do lewej (np. Arabski)
  9. W tym miejscu aktywujemy język

Możemy zapisać wprowadzone ustawienia klikając przycisk „Zapisz” (10).

Przechodzimy do zakładki „Lokalizacja” (1), następnie z listy wybierzmy kraj odpowiadający nowemu językowi (2), w tym przypadku United States.

Następnie naciskamy przycisk „Importuj” (3).

Tłumaczenie treści

Teraz edytując produkt znajdziemy nowe pole formularza zaznaczone na powyższym zrzucie ekranu.

Teraz edytując produkt znajdziemy nowe pole formularza zaznaczone na powyższym zrzucie ekranu.

Możemy w tym miejscu wybrać pozycję „en”.

Po przełączeniu języka możemy uzupełniać dane produktu, które będą wyświetlane tylko klientom korzystającym z niego.

W podobny sposób wygląda to podczas edycji i tworzenia kategorii.

Możemy oczywiście dodać kolejne języki do naszego sklepu i modyfikować treści w powyższy sposób.

Kategorie
Poradniki

Wielojęzyczny sklep WooCommerce

Aby dodać do naszego sklepu obsługę wielojęzyczności, należy zainstalować odpowiednią wtyczkę w tym celu.

Dlatego z menu bocznego wybierzmy „Wtyczki” (1), a następnie pozycję „Dodaj nową” (2).

W wyszukiwarce wpisujemy „polylang” (1), a następnie przy wtyczce „Polylang” klikamy przycisk „Zainstaluj teraz” (2).

Instalacja rozpocznie się automatycznie, aby aktywować plugin, należy nacisnąć przycisk „Atywuj”.

Po aktywowaniu zostaniemy przeniesieni do konfiguracji wtyczki.

W pierwszej kolejności z listy wybierzmy język, który chcemy obsługiwać (1), a następnie klikamy przycisk „Add new language” (2).

Język dodaje się na listę, możemy dodać kolejne języki, które chcemy obsługiwać.

Po ustawieniu języków możemy przejść dalej klikając przycisk „Continue” (3).

W kolejnym kroku możemy zadecydować, czy chcemy, aby tłumaczone były też teksty alternatywne, które mają być wyświetlane zamiast obrazów, gdy te nie wczytają się użytkownikowi na stronie (1).

Możem przejść dalej klikając przycisk „Continue” (2).

W tym miejscu wybieramy domyślny język stron (1), przechodzimy dalej klikając przycisk „Continue” (2)

Po konfiguracji możemy przejść do tłumaczenia klikając przycisk „View pages”.

Klikając przycisk „plus” w odpowiedniej kolumnie, aby dodać przetłumaczoną wersję strony.

Otworzy się standardowa strona edycji postu, z tym, że treść wypełniamy w danych języku (w tym przypadku w języku angielskim).

Możemy również tłumaczyć pojedyncze treści sklepu, w tym celu z menu bocznego wybierzmy pozycję „Languages” (1), a następnie pozycję „Strings translations” (2).

W tym miejscu w kolumnie najbardziej wysuniętej na prawo (1) wpisujemy tłumaczenie na język widoczny w kolumnie wcześniej.

Po uzupełnieniu tłumaczeń możemy zapisać zmiany klikając przycisk „Zapisz zmiany” (2)

Niestety za pomocą tej wtyczki, musimy wszystko tłumaczyć ręcznie i nie działa ona 100% tak jakbyśmy chcieli, za to jest darmowa.

Inną godną polecenia wtyczką jest WPML, przetłumaczy wiele treści automatycznie i działa bez zarzutów, niestety jest to płatna wtyczka.

Kategorie
Poradniki

Import i Eksport produktów WooCommerce

Aby eksportować i importować produkty w WooCommerce należy zainstalować specjalną wtyczkę.

W tym celu z menu bocznego wybierzmy pozycję „Wtyczki” (1), a następnie pozycję „Dodaj nowa” (2).

W wyszukiwarce (1) wpisujemy frazę „product import”, następnie przy wtyczce „Product Import Export for WooCommerce” klikamy przycisk „Zainstaluj teraz” (2).

Instalacja rozpocznie się, po jej zakończeniu klikamy przycisk „Aktywuj” przy wtyczce.

Po aktywowaniu zostaniemy automatycznie przeniesieni do eksportowania produktów. Znajdziemy tam następujące pola:

  1. Liczba produktów do pominięcia z początku listy.
  2. Maksymalna liczba wyeksportowanych produktów.
  3. W tym miejscu zaznaczamy, które pola chcemy wyeksportować, po prawej stronie możemy ustawić jak nazwane ma być dane pole w wyeksportowanym pliku.

Po ustawieniu powyższych danych klikamy przycisk „Export Products”.

Rozpocznie się pobieranie pliku, który otwieramy w programie do obsługi arkuszy kalkulacyjnych, np. Excel.

W pliku możemy znaleźć te same kolumny, który wyeksportowaliśmy z naszego sklepu.

W zaznaczonym powyżej wierszu został wpisany nowy produkt, który chcemy dodać przy imporcie.

Aby masowo edytować cenę produktów najlepiej zmienić cenę jednego z nich, następnie zaznaczyć komórkę i nacisnąć klawisz „Ctrl + C”, lub przycisk „Kopiuj” w lewym górnym rogu.

Następnie zaznaczamy wiersze w kolumnie Ceny dla wybranych produktów, którym chcemy ustawić identyczną cenę.

Kolejno naciskamy na klawiaturze klawisz „Ctrl + V” lub przycisk „Wklej” w lewym górnym rogu.

Po ustawieniu wszystkie możemy zapisać plik na dysku komputera.

Import produktów

Aby przejść do Importu produktów z górnych zakładek wtyczki wybieramy „Product Import”, znajdziemy tam następujące pola:

  1. W tym miejscu wybieramy nasz plik do wgrania
  2. Jeśli zaznaczymy tą opcję produkty które już istnieją zostaną zaktualizowane o wprowadzone dane.
  3. W tym miejscu wybieramy w jaki sposób są oddzielane kolejne kolumny, w pliku csv jest to „Semicolon”.
  4. Jeśli zaznaczymy tą opcję to wszystkie puste kolumny zostaną dodane do produktów z pustymi wartościami. Jeśli nie, zostaną pominięte.

Po ustawieniu wszystkiego możemy przejść dalej, klikając „Proceed to import Mapping”.

Na następnej stronie przypisujemy kolumny pliku do kolumn produktów, jeżeli importujemy przeedytowany, wcześniej wyeksportowany plik, to nie musimy tutaj wprowadzać żadnych zmian.

Po lewej widzimy nazwę kolumny produktu, na środku wybieramy kolumnę pliku.

Po prawej stronie możemy wpisać jak traktowana ma być wartość np.:

  • +2
  • -2
  • *2
  • /2

Jeśli ustawimy wszystko możemy zaimportować produkty klikając „Start Import”

Po wgraniu produktów, zobaczymy taki widok jak powyżej, informujący o udanym imporcie.