Hooki i Overrides PrestaShop: Przewodnik referencyjny dla deweloperów
Referencja hooków i nadpisań PrestaShop — hooki display i action, własne hooki, system nadpisań i migracja do nowoczesnych wzorców.
Czym są hooki i dlaczego są ważne
Hooki to system rozszerzeń PrestaShop — mechanizm, dzięki któremu moduły wchodzą w interakcję z rdzeniem platformy bez modyfikowania plików podstawowych. Każdy instalowany moduł opiera się na hookach, aby podłączyć się we właściwym miejscu i we właściwym czasie.
Jeśli kiedykolwiek zastanawiałeś się, skąd moduł „wie”, że ma wyświetlić coś pod ceną produktu, albo jak reaguje, gdy klient składa zamówienie — odpowiedzią są hooki.
Hooki są dla PrestaShop tym, czym gniazdka elektryczne są dla domu. Rdzeń dostarcza gniazdka w określonych miejscach, a moduły się do nich podłączają. Nie przerabiasz instalacji elektrycznej — podłączasz się tam, gdzie potrzebujesz zasilania.
Hooki Display vs hooki Action
PrestaShop posiada dwa rodzaje hooków:
- Hooki display (z prefiksem
display) — wizualne punkty wstawiania. Po wywołaniu zbierają HTML z zarejestrowanych modułów i renderują go. Przykład:displayProductAdditionalInfododaje treść poniżej ceny produktu. - Hooki action (z prefiksem
action) — powiadomienia o zdarzeniach. Nie generują wyjścia — informują moduły, że coś się wydarzyło. Przykład:actionCartSaveuruchamia się po aktualizacji koszyka.
Jak działa Hook Dispatcher
Gdy PrestaShop dociera do punktu hooka, wywołuje Hook::exec(), który wyszukuje zarejestrowane moduły w ps_hook_module, sortuje według pozycji, wywołuje metodę hooka każdego modułu z tablicą $params, a w przypadku hooków display łączy zwrócony HTML.
Najważniejsze hooki, które każdy deweloper powinien znać
Hooki display Front Office
| Nazwa hooka | Kiedy się uruchamia | Typowe zastosowanie |
|---|---|---|
displayHeader | Wewnątrz <head> na każdej stronie | Meta tagi, skrypty śledzenia |
displayFooter | Stopka strony | Widżety stopki, analityka |
displayHome | Obszar treści strony głównej | Banery, polecane produkty |
displayProductAdditionalInfo | Poniżej „Dodaj do koszyka” | Szacowany czas dostawy, tabele rozmiarów |
displayShoppingCart | Strona podsumowania koszyka | Cross-sell, kalkulatory wysyłki |
displayOrderConfirmation | Po złożeniu zamówienia | Śledzenie konwersji |
displayCustomerAccount | Strona „Moje konto” | Własne sekcje konta |
displayBanner | Góra strony, nad nagłówkiem | Ogłoszenia o promocjach |
Hooki display Back Office
| Nazwa hooka | Kiedy się uruchamia | Typowe zastosowanie |
|---|---|---|
displayBackOfficeHeader | Admin <head> | CSS/JS admina, powiadomienia |
displayAdminOrder | Strona szczegółów zamówienia | Własne panele, integracje kurierskie |
displayAdminProductsExtra | Zakładka edytora produktów | Własne pola produktu |
Hooki action
| Nazwa hooka | Kiedy się uruchamia | Typowe zastosowanie |
|---|---|---|
actionCartSave | Koszyk utworzony lub zaktualizowany | Rezerwacja stanów magazynowych, analityka |
actionOrderStatusUpdate | Zmiana statusu zamówienia | Powiadomienia, synchronizacja z ERP |
actionProductUpdate | Produkt zapisany w adminie | Synchronizacja z zewnętrznym katalogiem |
actionCustomerAccountAdd | Rejestracja nowego klienta | E-mail powitalny, synchronizacja z CRM |
actionValidateOrder | Zamówienie zwalidowane | Przetwarzanie płatności, aktualizacja stanu magazynowego |
actionFrontControllerSetMedia | Kontroler front ładuje zasoby | Rejestracja CSS/JS |
actionAdminControllerSetMedia | Kontroler admina ładuje zasoby | Rejestracja CSS/JS admina |
Rejestracja zasobów
Od PS 1.7 właściwym sposobem dodawania CSS/JS jest rejestracja zasobów wewnątrz actionFrontControllerSetMedia:
public function hookActionFrontControllerSetMedia($params)
{
$this->context->controller->registerStylesheet(
'my-module-style',
'modules/' . $this->name . '/views/css/front.css',
['media' => 'all', 'priority' => 150]
);
$this->context->controller->registerJavascript(
'my-module-script',
'modules/' . $this->name . '/views/js/front.js',
['position' => 'bottom', 'priority' => 150]
);
}
Umożliwia to optymalizację CCC (Combine, Compress, Cache) — znacznie lepsze rozwiązanie niż wstrzykiwanie surowych tagów przez displayHeader.
Jak używać hooków w module
Rejestracja hooków podczas instalacji
public function install()
{
return parent::install()
&& $this->registerHook('displayProductAdditionalInfo')
&& $this->registerHook('actionCartSave')
&& $this->registerHook('actionFrontControllerSetMedia');
}
Każde wywołanie wstawia wiersz do ps_hook_module. Jeśli zapomnisz o rejestracji, metoda hooka nigdy się nie uruchomi — nawet jeśli metoda istnieje.
Częsty problem przy debugowaniu: metoda hooka istnieje i jest poprawnie nazwana, ale moduł nigdy nie zarejestrował się na hooku podczas instalacji. Jeśli dodajesz hook po pierwszym wydaniu, potrzebujesz skryptu aktualizacji lub resetu modułu.
Implementacja metody hooka
Konwencja nazewnictwa metod: hook + nazwa hooka z wielką pierwszą literą.
public function hookDisplayProductAdditionalInfo($params)
{
$product = $params['product'];
$this->context->smarty->assign([
'delivery_estimate' => $this->calculateDeliveryEstimate($product),
]);
return $this->display(__FILE__, 'views/templates/hook/product-info.tpl');
}
// Hook action — brak wartości zwracanej
public function hookActionCartSave($params)
{
if (!isset($params['cart'])) {
return;
}
$this->logCartActivity($params['cart']->id);
}
Tablica $params
Zawartość różni się w zależności od hooka. Najczęstsze: $params['cart'], $params['order'], $params['product'], $params['customer']. Aby sprawdzić, co dany hook przekazuje, dodaj tymczasowe logowanie:
file_put_contents(
_PS_ROOT_DIR_ . '/var/logs/hook_debug.log',
print_r(array_keys($params), true),
FILE_APPEND
);
Znajdowanie dostępnych hooków
1. Tryb debugowania
Włącz w Advanced Parameters → Performance → Debug mode: YES. Front office będzie wyświetlać nazwy hooków w punktach wstawiania. W PS 8+ pasek debugowania Symfony pokazuje wszystkie hooki wywołane w ramach zapytania.
Nigdy nie włączaj trybu debugowania na sklepie produkcyjnym. Ujawnia dane wewnętrzne i spowalnia stronę.
2. Zapytanie do bazy danych
-- All hooks with "product" in the name
SELECT name, title FROM ps_hook WHERE name LIKE '%product%' ORDER BY name;
-- Which modules are on a specific hook
SELECT m.name, hm.position FROM ps_hook_module hm
JOIN ps_hook h ON h.id_hook = hm.id_hook
JOIN ps_module m ON m.id_module = hm.id_module
WHERE h.name = 'displayProductAdditionalInfo'
ORDER BY hm.position;
3. Przeszukiwanie szablonów i kodu źródłowego
# Display hooks in theme templates
grep -rn "{hook " themes/your-theme/templates/
# Action hooks in PHP core
grep -rn "Hook::exec" classes/ controllers/ src/
Tworzenie własnych hooków
Gdy wbudowane hooki nie uruchamiają się dokładnie w potrzebnym momencie, utwórz własne:
// Dispatch a custom hook
$hookResult = Hook::exec('actionMyModuleBeforeProcess', [
'order_id' => $orderId,
'custom_data' => $myData,
]);
// For display hooks — true returns array per module
$extraHtml = Hook::exec('displayMyModuleExtraContent', [
'product' => $product,
], null, true);
Zarejestruj go w install() tak jak każdy inny hook. Aby nadać mu przyjazną nazwę w Positions:
$hook = new Hook();
$hook->name = 'displayMyModuleExtraContent';
$hook->title = 'My Module - Extra Content Area';
$hook->add();
Twórz własne hooki, gdy Twój moduł oferuje punkty rozszerzeń, których potrzebują inne moduły. Nie twórz ich do wewnętrznej logiki — używaj zwykłych metod.
System override
Override zastępują klasy i kontrolery rdzenia bez modyfikowania plików podstawowych. Były główną metodą personalizacji w PS 1.5/1.6, zanim hooki stały się wszechstronne.
Jak działają override
Override rozszerza klasę rdzenia i zastępuje określone metody. Autoloader sprawdza najpierw katalog override/:
// override/classes/Cart.php
class Cart extends CartCore
{
public function getOrderTotal($with_taxes = true, $type = Cart::BOTH,
$products = null, $id_carrier = null, $use_cache = false)
{
$total = parent::getOrderTotal($with_taxes, $type, $products, $id_carrier, $use_cache);
if ($this->hasSpecialProducts()) {
$total += $this->calculateSurcharge();
}
return $total;
}
}
Struktura katalogów odzwierciedla rdzeń: override/classes/, override/controllers/front/, override/controllers/admin/.
Override vs hook
| Scenariusz | Podejście |
|---|---|
| Dodanie treści na stronę | Hook display |
| Reakcja na zdarzenie | Hook action |
| Dodanie CSS/JS | Hook rejestracji zasobów |
| Zmiana obliczeń rdzenia | Override (jeśli nie istnieje hook) |
| Modyfikacja zapytania SQL rdzenia | Override (jeśli nie istnieje hook) |
Problemy z override
- Konflikty: Dwa moduły nie mogą nadpisać tej samej metody. Drugi nie zainstaluje się.
- Awarie przy aktualizacji: Sygnatury metod rdzenia zmieniają się między wersjami. Override dla PS 1.7.7 może wywołać błąd na 1.7.8.
- Ukryte zachowanie: Override są niewidoczne z Back Office, co utrudnia debugowanie.
- Uszkodzenie indeksu: Mapowanie
class_index.phpmoże ulec uszkodzeniu, psując cały sklep.
Zespół PrestaShop aktywnie odradza używanie override od wersji 1.7. Jeśli sięgasz po override, najpierw sprawdź, czy istnieje hook lub alternatywa oparta na Symfony.
Alternatywy dla override (PS 8+)
Dekoratory serwisów Symfony
Opakowuj serwisy rdzenia własną logiką — wiele dekoratorów może działać w łańcuchu bez konfliktów:
# modules/mymodule/config/services.yml
services:
mymodule.decorated_calculator:
class: MyModule\Service\CalculatorDecorator
decorates: 'prestashop.core.cart.calculator'
arguments:
- '@mymodule.decorated_calculator.inner'
Doctrine Event Listeners
Reaguj na zmiany encji bez nadpisywania ObjectModel:
# modules/mymodule/config/services.yml
services:
mymodule.product_listener:
class: MyModule\EventListener\ProductListener
tags:
- { name: doctrine.event_listener, event: postUpdate }
CQRS Commands and Queries
Wpływaj na operacje admina za pomocą handlerów poleceń i zapytań. To najnowocześniejsze podejście, wymagające znajomości architektury Symfony w PrestaShop.
Rozwiązywanie konfliktów override
Gdy dwa moduły nadpisują tę samą metodę, zobaczysz:
The method Cart::getOrderTotal is already overridden by module "othermodule".
Rozwiązaniem jest ręczne scalanie — połącz logikę obu modułów w jednym pliku override. Po edycji przebuduj indeks klas:
# Delete class index — PrestaShop rebuilds automatically
rm var/cache/prod/class_index.php
rm var/cache/dev/class_index.php
Ręczne scalanie override jest kruche. Gdy którykolwiek moduł się zaktualizuje, możesz potrzebować ponownego scalenia. Dokumentuj, co scałeś i dlaczego.
Jeśli moduł używa override tam, gdzie wystarczyłby hook, skontaktuj się z deweloperem i poproś o rozwiązanie oparte na hookach.
Priorytet hooków i kolejność wykonywania
Gdy wiele modułów jest zarejestrowanych na tym samym hooku display, pozycja określa kolejność. Zarządzaj tym w Design → Positions, gdzie możesz przeciągać moduły, odłączać je lub przenosić do innych hooków.
Transplantacja
Przenieś moduł z jednego hooka do drugiego przez Design → Positions → Transplant a Module. Nie każda transplantacja wyświetla się poprawnie — zależy to od szablonów modułu.
Programowe ustawianie pozycji
// In install() — move to position 1
$this->updatePosition($this->getHookId('displayHome'), false, 1);
Debugowanie hooków
Systematyczne sprawdzanie
-- Is the module registered on the hook?
SELECT m.name, hm.position FROM ps_hook_module hm
JOIN ps_hook h ON h.id_hook = hm.id_hook
JOIN ps_module m ON m.id_module = hm.id_module
WHERE h.name = 'displayProductAdditionalInfo' AND m.name = 'your_module_name';
-- Is the module active?
SELECT name, active FROM ps_module WHERE name = 'your_module_name';
Częste problemy
| Objaw | Przyczyna | Rozwiązanie |
|---|---|---|
| Metoda nigdy nie jest wywoływana | Brak rejestracji na hooku | Sprawdź ps_hook_module; zresetuj moduł |
| Zwraca pusty ciąg | Nie znaleziono szablonu | Sprawdź ścieżkę szablonu, włącz błędy |
| Uruchamia się na złych stronach | Brak sprawdzania strony | Dodaj instanceof ProductController |
| Uruchamia się dwukrotnie | Podwójna rejestracja | Sprawdź zduplikowane wiersze |
| $params['product'] jest tablicą | PS 1.7+ używa tablic | Użyj $params['product']['id_product'] |
Profilowanie hooków
Włącz profilowanie, aby zobaczyć czas wykonania każdego hooka:
// PS 1.7 — defines.inc.php
define('_PS_DEBUG_PROFILING_', true);
// PS 8+ — .env.local
APP_DEBUG=1
APP_ENV=dev
Zmiany w PrestaShop 9
Nowe hooki
PS 9 dodaje hooki w obszarach, które wcześniej wymagały override: rozszerzenia formularza produktów w adminie, zastosowanie reguł cenowych koszyka, operacje na zasobach API oraz proces wysyłania e-maili.
Przestarzałe hooki
Hooki powiązane ze starszymi kontrolerami admina są oznaczone jako przestarzałe, ponieważ te kontrolery migrują do Symfony. Sprawdź _PS_DEPRECATED_HOOKS_, aby zobaczyć pełną listę.
Przyszłość systemu override
Override nadal działają dla starszych klas ObjectModel, ale nowe serwisy Symfony i kontrolery admina nie mogą ich używać. System override zostanie ostatecznie usunięty wraz z zakończeniem migracji do Symfony.
Jeśli zaczynasz nowy moduł dzisiaj, buduj wyłącznie z hookami i serwisami Symfony. Każdy override, który teraz napiszesz, to dług techniczny, który później usuniesz.
Szybka ściąga
// Register hooks (install)
$this->registerHook(['displayHeader', 'actionCartSave']);
// Check if hook exists
$hookId = Hook::getIdByName('displayMyCustomHook');
// Get modules on a hook
$modules = Hook::getHookModuleExecList('displayHeader');
// Execute hook manually
$output = Hook::exec('displayMyHook', ['key' => 'value']);
// Conditional execution
public function hookDisplayHeader($params) {
if (!($this->context->controller instanceof ProductController)) return '';
return $this->display(__FILE__, 'views/templates/hook/header.tpl');
}
-- All hooks a module is registered on
SELECT h.name, hm.position FROM ps_hook_module hm
JOIN ps_hook h ON h.id_hook = hm.id_hook
JOIN ps_module m ON m.id_module = hm.id_module
WHERE m.name = 'your_module_name' ORDER BY h.name;
-- Hooks with no modules registered
SELECT h.name FROM ps_hook h
LEFT JOIN ps_hook_module hm ON h.id_hook = hm.id_hook
WHERE hm.id_hook IS NULL ORDER BY h.name;
Opanuj najpierw hooki — pokrywają zdecydowaną większość przypadków użycia. Poznaj override, aby rozumieć starszy kod i rozwiązywać konflikty. W PrestaShop 8+ inwestuj w alternatywy oparte na Symfony. Ekosystem idźie do przodu, a hooki wiodą prym.
More guides available
Browse our knowledge base for more practical PrestaShop tutorials, or reach out if you need help.