Files
newwalls.pl/plan.md
Jacek Pyziak e888c81aef Add product carousel module with template and database structure
- Created `pp_carousel.tpl` for rendering product carousel with Swiper integration.
- Added `plan.md` detailing module architecture, database schema, and implementation steps.
- Initialized log files for development and production environments.
2026-02-25 09:23:54 +01:00

8.6 KiB

Plan: Moduł Project-Pro Karuzela Produktów

Analiza screenshota

Karuzela wyświetla produkty jako karty z: dużym zdjęciem, etykietą kategorii (label w lewym górnym rogu), nazwą produktu, ceną z sufiksem /m2. Układ 3 kolumny na desktop, slider Swiper.

Kluczowe decyzje architektoniczne

Dlaczego tabela SQL zamiast Configuration? Moduł wymaga dowolnej liczby karuzeli — Configuration przechowuje pary klucz-wartość, nie nadaje się do list rekordów o zmiennej długości. Dedykowana tabela pp_carousel to jedyne poprawne rozwiązanie.

Dlaczego dynamiczne hooki? Użytkownik chce przypisywać karuzele do hooków, w tym niestandardowych. Moduł rejestruje się na displayHeader + catch-all actionDispatcher, a wyświetlanie realizuje poprzez Hook::exec() z warunkiem WHERE hook_name = ? w bazie. Niestandardowe hooki dodawane są przez Hook::getIdByName() / ręczne INSERT.

Struktura plików modułu

modules/pp_carousel/
├── pp_carousel.php                    # Główna klasa modułu
├── config.xml
├── logo.png
├── sql/
│   ├── install.sql                    # CREATE TABLE
│   └── uninstall.sql                  # DROP TABLE
├── views/
│   ├── css/
│   │   └── pp_carousel.css            # Style karuzeli (front)
│   ├── js/
│   │   └── pp_carousel.js             # Inicjalizacja Swiper (front)
│   ├── lib/
│   │   └── swiper/                    # swiper-bundle.min.js/css (kopiujemy z project_pro_news)
│   └── templates/
│       ├── hook/
│       │   └── pp_carousel.tpl        # Szablon frontu karuzeli
│       └── admin/
│           ├── list.tpl               # Lista karuzeli w BO
│           └── form.tpl               # Formularz dodawania/edycji karuzeli
Kolumna Typ Opis
id_carousel INT AUTO_INCREMENT PK ID karuzeli
hook_name VARCHAR(128) Nazwa hooka (np. displayHome, custom)
source_type ENUM('new','bestseller','category','manual') Źródło produktów
id_category INT DEFAULT 0 ID kategorii (gdy source_type=category)
product_ids TEXT Ręczne ID produktów rozdzielone przecinkami
limit_products INT DEFAULT 12 Limit produktów
title VARCHAR(255) Tytuł karuzeli
subtitle VARCHAR(255) Podtytuł
button_label VARCHAR(255) Tekst przycisku
button_url VARCHAR(512) URL przycisku
price_suffix VARCHAR(64) Sufiks ceny (np. /m²)
position INT DEFAULT 0 Kolejność sortowania
active TINYINT(1) DEFAULT 1 Aktywna/nieaktywna
id_shop INT DEFAULT 1 Multishop
date_add DATETIME Data utworzenia
date_upd DATETIME Data modyfikacji

Wielojęzyczność: pola title, subtitle, button_label, price_suffix — dodatkowa tabela ps_pp_carousel_lang z kolumnami id_carousel, id_lang, title, subtitle, button_label, price_suffix.

Implementacja krok po kroku

Krok 1: Plik główny pp_carousel.php — szkielet

  • Klasa Pp_Carousel extends Module
  • Konstruktor: name=pp_carousel, author=Project-Pro, version=1.0.0, displayName=Project-Pro Karuzela Produktów
  • install(): wykonuje SQL, rejestruje hooki: displayHeader, displayHome, displayBackOfficeHeader
  • uninstall(): DROP tabeli, usunięcie konfiguracji
  • Metoda getContent() → panel admin z listą karuzeli + formularz CRUD

Krok 2: SQL install/uninstall

install.sql:

CREATE TABLE IF NOT EXISTS `PREFIX_pp_carousel` (
    `id_carousel` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
    `hook_name` VARCHAR(128) NOT NULL DEFAULT 'displayHome',
    `source_type` VARCHAR(20) NOT NULL DEFAULT 'new',
    `id_category` INT(11) UNSIGNED NOT NULL DEFAULT 0,
    `product_ids` TEXT,
    `limit_products` INT(11) UNSIGNED NOT NULL DEFAULT 12,
    `button_url` VARCHAR(512) DEFAULT '',
    `position` INT(11) UNSIGNED NOT NULL DEFAULT 0,
    `active` TINYINT(1) UNSIGNED NOT NULL DEFAULT 1,
    `id_shop` INT(11) UNSIGNED NOT NULL DEFAULT 1,
    `date_add` DATETIME NOT NULL,
    `date_upd` DATETIME NOT NULL,
    PRIMARY KEY (`id_carousel`),
    KEY `hook_name` (`hook_name`),
    KEY `active` (`active`)
) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8mb4;

CREATE TABLE IF NOT EXISTS `PREFIX_pp_carousel_lang` (
    `id_carousel` INT(11) UNSIGNED NOT NULL,
    `id_lang` INT(11) UNSIGNED NOT NULL,
    `title` VARCHAR(255) DEFAULT '',
    `subtitle` VARCHAR(255) DEFAULT '',
    `button_label` VARCHAR(255) DEFAULT '',
    `price_suffix` VARCHAR(64) DEFAULT '',
    PRIMARY KEY (`id_carousel`, `id_lang`)
) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8mb4;

Krok 3: Panel administracyjny (getContent())

Widok listy (domyślny):

  • Tabela HelperList: ID | Tytuł | Hook | Źródło | Limit | Aktywna | Akcje (edytuj/usuń)
  • Przycisk "Dodaj karuzelę"

Widok formularza (dodaj/edytuj):

  • Hook — select z listą dostępnych hooków + pole tekstowe "Niestandardowy hook" (jeśli wpisany, tworzony jest nowy hook w ps_hook)
  • Źródło produktów — select: Nowości / Bestsellery / Kategoria / Ręczne ID
  • Kategoria — select z drzewem kategorii (widoczny gdy source_type=category)
  • Ręczne ID produktów — textarea, oddzielone przecinkami (widoczny gdy source_type=manual)
  • Limit produktów — input number
  • Tytuł — input text (wielojęzyczny)
  • Podtytuł — input text (wielojęzyczny)
  • Tekst przycisku — input text (wielojęzyczny)
  • URL przycisku — input text
  • Sufiks ceny — input text (wielojęzyczny), np. /m²
  • Aktywna — switch tak/nie

Krok 4: Pobieranie produktów — metoda getProductsByCarousel($carousel)

Switch po source_type:

  • newProduct::getNewProducts($idLang, 0, $limit) (wzorzec z ps_newproducts)
  • bestsellerProductSale::getBestSales($idLang, 0, $limit) (wzorzec z ps_bestsellers)
  • categoryCategory::getProducts($idCategory, $idLang, 1, $limit) (wzorzec z project_pro_news)
  • manualSELECT po id_product IN (...), potem prezentacja

Wszystkie wyniki przechodzą przez ProductAssembler + ProductListingPresenter (identycznie jak w istniejących modułach).

Krok 5: Renderowanie hooków

Mechanizm: moduł rejestruje się na najczęstsze hooki (displayHome, displayFooterBefore, displayLeftColumn itp.). W każdym hooku:

public function hookDisplayHome($params) {
    return $this->renderCarouselsForHook('displayHome');
}

Metoda renderCarouselsForHook($hookName):

  1. SELECT z ps_pp_carousel WHERE hook_name = $hookName AND active = 1 ORDER BY position
  2. Dla każdej karuzeli: pobierz produkty, przypisz do Smarty, fetch szablonu
  3. Zwróć połączony HTML

Niestandardowe hooki: przy zapisie karuzeli z nowym hookiem → $this->registerHook($hookName) + ewentualnie INSERT do ps_hook.

Krok 6: Szablon frontu pp_carousel.tpl

Wzorowany na project_pro_news.tpl — ten sam layout Swiper:

  • Section z unikalnym id="pp-carousel-{$carousel.id}"
  • Nagłówek: tytuł + podtytuł
  • Swiper container z kartami produktów
  • Karta: zdjęcie (home_default), etykieta kategorii, nazwa, cena + sufiks
  • Nawigacja prev/next
  • Przycisk "Zobacz więcej"

Krok 7: CSS + JS

CSS — bazowany na project_pro_news.css, dostosowany do nowego namespace .pp-carousel.

JS — inicjalizacja wielu instancji Swipera:

document.querySelectorAll('.pp-carousel__slider').forEach(function(el) {
    new Swiper(el, { /* config */ });
});

Krok 8: Rejestracja assetów

hookDisplayHeader() — rejestracja CSS/JS (swiper + pp_carousel) identycznie jak w project_pro_news.

Hooki do rejestracji (install)

Obowiązkowe:

  • displayHeader (assety CSS/JS)
  • displayHome (główna pozycja)
  • displayBackOfficeHeader (admin CSS/JS jeśli potrzebne)

Dodatkowe (rejestrowane dynamicznie przy tworzeniu karuzeli z niestandardowym hookiem):

  • displayFooterBefore, displayTopColumn, displayLeftColumn, displayFooter itd.
  • Dowolne niestandardowe hooki

Kolejność wdrożenia

  1. Pliki SQL — tabele pp_carousel + pp_carousel_lang
  2. pp_carousel.php — konstruktor, install/uninstall, hookDisplayHeader
  3. Panel admin — getContent() z listą + formularzem CRUD
  4. Logika produktów — getProductsByCarousel() z 4 źródłami
  5. Rendering hooków — renderCarouselsForHook() + rejestracja dynamiczna
  6. Szablon .tpl — karta produktu zgodna ze screenshotem
  7. CSS/JS — style + Swiper init (wieloinstancyjny)
  8. Testowanie — dodanie karuzeli w BO, weryfikacja na froncie