eExtend is live: chatbot, translation and content AI in one subscription. 50% off for the first 100 → Code: Launch26
How Symfony Core Updates in PrestaShop 9 Impact Custom Database Hook Overrides
Learn how Symfony core updates in PrestaShop 9 affect database hook overrides and how to migrate them to services. Practical steps and pitfalls to avoid.

In PrestaShop 9, the Symfony core updates fundamentally change how hooks are dispatched and overridden. Traditional database hook overrides (stored in the ps_hook and ps_hook_alias tables) are now managed by the Symfony service container and HookDispatcher. To migrate, you must refactor your custom overrides into dedicated Symfony services that subscribe to hooks, then register them in your module's services.yml file. This ensures full compatibility with the new architecture and avoids silent failures.

The PrestaShop 9.0 back office has been fully migrated to Symfony controllers and Twig templates, replacing older frameworks and Smarty-based rendering. That migration is not cosmetic. It changes the execution path your module's code travels, and hooks sit squarely in that path. What worked as a database-driven override in PrestaShop 8 may simply not fire in 9.

Here is the practical difference. In legacy versions, PrestaShop looked up hook registrations in the database, matched them to module methods, and dispatched directly. In PrestaShop 9, the HookDispatcher (part of the Symfony event system) resolves subscribers through the service container instead. Your module's hooks must be registered as services with tagged methods, not just inserted into the hook tables.

This is not an isolated change. The Symfony migration roadmap for core releases has been in progress since September 2018, and each major version moves more functionality away from legacy dispatch. Developers who skipped intermediate upgrades are now facing a larger refactor than those who migrated incrementally. The official hooks documentation for versions 1.7, 8 and 9 tracks these differences, and the gap between 8 and 9 is the widest yet.

The core problem is that database hook overrides were never designed to survive a framework migration. They relied on procedural dispatch logic that Symfony does not replicate. If you leave your overrides in the database and upgrade, your hooks will not throw errors; they will simply not execute.

Database hook overrides are a legacy pattern, and PrestaShop 9 will not honour them.

One earlier regression shows how fragile this area already was. Version 1.7.8.4 changed the code for retrieving hooks from the database and introduced a regression in hook management. If a patch release in the 1.7 line could break hook retrieval, a full Symfony core migration in version 9 is a far larger risk surface.

What this means for your migration plan:

  • Audit every module that registers hooks via install() methods or direct database inserts, not just the ones you wrote.
  • Identify which hooks are genuinely overridden versus merely subscribed. Overrides change behaviour; subscriptions respond to events.
  • Prepare to move each override into a Symfony service with the appropriate prestashop.hook tag before you run the 9.0 upgrade.

Once you have completed the service refactor, your hooks dispatch through the container and behave predictably. But the migration only works if you also respect PrestaShop 9's stricter system requirements, including a minimum of MySQL 5.7 or MariaDB 10.2 so confirm your hosting environment can support the new core before you begin.

What's in this guide
  1. Why Your Custom Database Hook Overrides Are Breaking (or About To)
  2. How Do I Migrate My Database Hook Overrides to Symfony Services?
  3. Step-by-Step Migration of Database Hook Overrides to PrestaShop 9
  4. How Does This Differ From PrestaShop 8 and 1.7 Overrides?
  5. Common Pitfalls to Avoid When Migrating Hook Overrides
  6. Testing Your Overrides in PrestaShop 9: A Quick Checklist
  7. Why PrestaShop 9 Breaks Traditional Hook Overrides
  8. The 1.7.8.4 Regression: A Warning Sign
  9. How Hook Registration Works in PrestaShop 9
  10. Audit Your Existing Database Hook Overrides
  11. Migration Path for Custom Hook Logic

Why Your Custom Database Hook Overrides Are Breaking (or About To)

If your modules still register hooks by writing directly to the database, PrestaShop 9 will quietly ignore them. The back office has now been fully migrated to Symfony controllers and Twig templates, replacing older frameworks and Smarty-based rendering entirely. That migration means the old override patterns your custom database hook relied on are no longer part of the request lifecycle.

There are three common failure points you will hit when moving a module from PrestaShop 8 to 9. Each one produces a different symptom, and knowing which one you are dealing with saves hours of debugging.

  • Deprecated override files are ignored. Overrides placed in override/classes or override/controllers that target legacy core classes no longer load because those classes have been replaced by Symfony services. The file exists, but nothing calls it.
  • Hook registration is ignored. Modules that call $this->registerHook() against hook names not defined in the new hook system simply fail silently. The action hooks, display hooks and filter hooks are now defined per version, and a name that worked in 1.7 may not exist in 9.
  • The service container is not aware of your custom hooks. Even if your module registers a hook name correctly, the Symfony container must know how to dispatch it. If your module does not declare a service for the hook listener, the event never fires.

Detecting these issues is straightforward. Enable Symfony debug mode and check the logs for "unknown hook" warnings. Then inspect var/logs for deprecation notices about override classes. A module that worked on PrestaShop 1.7 with Symfony development training practices may need a full refactor, not a small patch.

The fastest way to confirm the break is to run a hook listing before and after the upgrade and diff the output. If your custom hooks appear in the database table ps_hook but never execute, the problem is dispatch, not registration.

One historical regression is worth noting here. Version 1.7.8.4 changed the code that retrieves hooks from the database, and that change introduced its own issues that required a dedicated fix. That history is a warning: the hook retrieval path has already been fragile once, and PrestaShop 9's Symfony core is a much larger rewrite of the same system.

You also need to plan for the environment, not just the code. PrestaShop 9 system requirements now mandate SQL MySQL 5.7 minimum or MariaDB 10.2 minimum, with a recent version recommended. Older database versions can fail on the migration scripts that rebuild hook tables, and a partial migration leaves your custom database hook overrides half-registered.

The PrestaShop 9.0 upgrade notes confirm that the back office migration to Symfony controllers and Twig templates is complete, not partial. Treat every hook override as potentially broken until you have verified it in a staging environment running the new core. The migration path that preserves your custom functionality starts with auditing which of these three failure points your modules actually hit.

How Do I Migrate My Database Hook Overrides to Symfony Services?

The migration path moves your logic out of the database and into a dedicated Symfony service class. PrestaShop 9's back office now runs entirely on Symfony controllers and Twig templates, so hook subscribers registered as services integrate cleanly with the modern architecture instead of fighting it.

Start by creating a service class in your module's directory, typically src/Listener/ or src/Handler/. This class will contain the public methods that respond to specific hooks. Name the methods clearly, such as handleDisplayProductList() or onActionObjectProductUpdate(), so the mapping between hook name and method stays obvious even after years of maintenance.

The single most important shift is registering your hook handlers as Symfony services rather than letting them live as database rows. This gives you dependency injection, testability, and a clear upgrade path that survives future core changes.

Step 1: Build Your Service Class

Create a plain PHP class inside your module. It receives dependencies through its constructor, which Symfony resolves automatically. For example, a hook that needs to read configuration or log activity can accept those services as constructor arguments without any manual wiring.

Keep each hook method focused. A method should handle one hook and one responsibility, which makes debugging far easier when a particular hook misbehaves after an upgrade.

Step 2: Define the Service in services.yml

Inside your module's config/services.yml, register the class as a service. The service definition tells PrestaShop's service container that this class exists, and, critically, that it should be tagged as a hook subscriber using prestashop.hook as the tag name. The tag is what connects your service methods to the HookDispatcher.

Your service definition should look something like this:

services:
  module_name.handler.custom_hooks:
    class: Module\ModuleName\Handler\CustomHooksHandler
    public: true
    tags:
      - { name: prestashop.hook, method: handleDisplayProductList, hook: displayProductList }

Each tag entry maps one hook to one method. If you handle three hooks, you list three tag entries on the same service. This declarative approach replaces the old hookDisplayProductList() convention that relied on the database lookup.

Step 3: Register the Hooks Without Database Overrides

In your module's main class, keep using the install() method to register hooks via registerHook(). This registration is still required so PrestaShop knows which hooks your module claims. The difference is that the implementation now resolves through the service tag instead of a database override.

Remove any manual entries you previously inserted into the hooks and hook_alias database tables. Those overrides are what the complete PrestaShop hooks documentation for versions 1.7, 8, and 9 warns against carrying forward, since the retrieval logic changed in version 1.7.8.4 and the Symfony core now bypasses much of that path.

Edge Cases to Watch

  • Hook arguments: Symfony hook handlers receive the hook parameters as a single array. Respect that signature rather than expecting individual arguments passed positionally.
  • Return values: Display hooks must return strings (usually rendered Twig templates). Action hooks typically return nothing. Mixing these up causes silent failures that are hard to trace.
  • Service scope: Keep your service public if other modules or overrides need to access it. Private services work fine for hooks but restrict external access.
  • Cache clearing: After editing services.yml, clear the cache in the back office or via the console (php bin/console cache:clear). The container caches service definitions aggressively.

The Symfony development training for PrestaShop 1.7 covers this service registration pattern in depth, and the approach carries directly into version 9. If you manage multiple client shops with custom hook logic, the migration cost pays for itself the first time a minor core update ships without breaking your overrides.

Test each hook individually after migration. Create a test product, place a test order, and exercise the display pages that your hooks affect. The old database overrides often masked problems because they were resolved lazily; service-based hooks fail fast, which is a feature, not a bug.

Final Considerations

The migration path described above removes the deprecated database lookup entirely. Your custom functionality survives the Symfony core upgrade because it now lives in a first-class Symfony citizen: a tagged service consumed by the HookDispatcher. The PrestaShop 9 back office migration to full Symfony controllers means older override patterns will keep breaking with each release, so adopting this structure now protects your modules against future churn.

Step-by-Step Migration of Database Hook Overrides to PrestaShop 9

Migrating your database hook overrides to PrestaShop 9 follows a predictable pattern. Work through these steps in order, testing at each stage rather than attempting a single big-bang migration. The full PrestaShop hooks documentation for versions 1.7, 8 and 9 lists every available hook with usage examples, so keep it open as your reference while you work.

Step 1: Identify existing overrides

Start by auditing which hooks your module currently overrides. Search your module directory for calls to hookDisplay*, hookAction* and hookFilter* methods, and cross-reference those against the hooks your module registers in its main file. If you inherited the module from an older project, also check the ps_hook and ps_hook_module database tables for orphaned entries that no longer match any module code.

Document each hook with three details: the hook name, the context in which it fires, and any data it receives. This inventory becomes your checklist for the refactor. Version 1.7.8.4 changed how hooks are retrieved from the database, and a known regression from that release affected hook management so be prepared to find legacy behaviour that no longer applies cleanly in 9.

Step 2: Refactor hook logic into a service class

Move the body of each hook method into a dedicated service class. The Symfony pattern expects your business logic to live outside the module's main class, with the module file acting only as a thin routing layer. Create a class such as CartDisplayService or ProductActionService under src/ in your module namespace, and give it a public method that accepts the hook parameters.

<?php
namespace MyModule\Service;

class CartDisplayService
{
    public function handle(array $params): string
    {
        // Your existing hook logic, now decoupled from the module class
        return $this->render();
    }
}

This separation matters because PrestaShop 9's back office relies on Symfony controllers and Twig templates rather than the older rendering stack. Logic that sits inside the module class is harder to test and harder to maintain once the surrounding framework changes.

Step 3: Register the service in services.yml

Declare your new class as a service in the module's config/services.yml file. Give it a clear ID that matches your module naming conventions, and let Symfony autowire any dependencies. The registration below makes the service available to your module and to any Symfony controller that needs it.

services:
    mymodule.service.cart_display:
        class: MyModule\Service\CartDisplayService
        public: true

If your service depends on PrestaShop's own services, such as the database connection or the context, add those as constructor arguments and let autowiring resolve them. This keeps your module aligned with the dependency injection patterns used across the upgraded core.

Step 4: Update the module hooks and clear cache

Replace the body of your module's hook methods with a call to the service. The module file now delegates to the registered service, which keeps the hook signature intact while the implementation moves to a testable location.

public function hookDisplayCartExtra(array $params)
{
    return $this->get('mymodule.service.cart_display')->handle($params);
}

After updating the code, clear the cache from the back office under Advanced Parameters, Performance, or delete the var/cache directory manually. Then reinstall the module or reset it to refresh the hook registrations. Verify each migrated hook by loading the page where it fires and checking that the output appears exactly once.

Pay attention to hooks that fire during AJAX requests or in the back office, since these often expose subtle differences between the legacy and Symfony execution paths. Test on a staging environment first, and check the PrestaShop 9 system requirements before you start so your server meets the minimum SQL and PHP versions.

The migration is complete when every hook from your Step 1 inventory delegates to a service, the cache is cleared, and all pages render correctly. From that point forward, new hooks you add in PrestaShop 9 should follow the same service pattern rather than returning to the old inline method style. This approach survives future core updates because it does not depend on the legacy hook dispatch behaviour that the Symfony migration continues to phase out.

How Does This Differ From PrestaShop 8 and 1.7 Overrides?

PrestaShop 9 marks a genuine break with the past. The back office has been fully migrated to Symfony controllers and Twig templates replacing older frameworks and Smarty-based rendering. That migration changes how modules hook into core behaviour, and it directly affects the way your database hooks are resolved.

In PrestaShop 1.7 and 8, the override system was forgiving. You could register a hook, override a controller or object model class, and the legacy dispatcher would pick up your modifications. The database hook registry was read at runtime, and your custom entries were honoured with minimal friction. In PrestaShop 9, the Symfony container becomes the source of truth, and database-only hook registrations no longer behave the way they used to.

The practical difference comes down to service registration. Legacy overrides relied on class replacement at file level. PrestaShop 9 expects you to declare your hooks as Symfony services, with explicit tags that tell the dispatcher what each service does. If you have not made that transition, your database entries are effectively orphaned.

Why This Breaks Existing Modules

  • Version 1.7.8.4 changed the code for retrieving hooks from the database, and that change introduced a regression that affected custom hook management. The fix was released but the underlying retrieval logic remained fragile.
  • PrestaShop 9 reads hook registrations from the compiled Symfony container. Entries that exist only in the ps_hook table are ignored unless they are also declared in a module's service configuration.
  • Order processing changes in the SEO & URL settings and other core areas now route through Symfony controllers. A legacy override that targets a Smarty template simply never fires.

How Each Version Should Approach the Migration

Version What Breaks Recommended Path
1.7.x Database hook retrieval changed in 1.7.8.4; custom hooks may not persist correctly. Keep overrides where possible, but begin registering hooks as services to future-proof.
8.x Symfony adoption is partial; mixed behaviour across legacy and modern controllers. Migrate hook registrations to config/services.yml and tag them explicitly.
9.x Legacy database overrides are not honoured by Symfony controllers. Full service-based migration. Database entries become a fallback, not the primary mechanism.

The migration effort is not uniform. For 1.7 stores, you can stagger the work: fix the database retrieval regression, then gradually move hooks into service declarations. For PrestaShop 8, the mixed environment means you must test each hook individually to confirm which dispatcher handles it. PrestaShop 9 leaves no ambiguity, the Symfony container is the only path.

Before you start, review the complete PrestaShop hooks list and documentation for versions 1.7, 8 and 9 to confirm which hooks you actually use. Many modules declare hooks that are never called, and those are the safest to drop during migration. This also reduces the surface area for compatibility issues when you finally upgrade.

Common Pitfalls to Avoid When Migrating Hook Overrides

Most migration failures don't come from misunderstanding Symfony. They come from small, avoidable mistakes that surface only after the module is live. Here are the four most common traps we see when developers move database hook overrides to the PrestaShop 9 core.

Forgetting to clear the cache is the single most common cause of "broken" hook overrides after migration. Symfony's service container caches aggressively. If you register a new service and don't run php bin/console cache:clear, the old container stays in memory and your hook never fires. The same applies to the Smarty template cache in the back office.

A second frequent error involves service tags. In PrestaShop 9, a hook listener only works when its service is tagged correctly in config/services.yml. The tag must reference the exact hook name, and the tag name itself follows a strict convention. Copying an example from PrestaShop 8 without checking the PrestaShop hooks list and documentation for versions 1.7, 8 and 9 is a reliable way to introduce a silent failure.

Third, pay attention to the parameters your hook method accepts. A display hook expects different arguments than a filter hook. Passing the wrong type, or assuming an outdated signature, produces runtime errors that are hard to trace because they appear in the Symfony logs, not the PrestaShop error log.

Finally, remember that Symfony's service container enforces dependency injection rules. If your service declares a dependency on another service, that dependency must be defined in the container. The PrestaShop 9 back office has been fully migrated to Symfony controllers and Twig templatesso the container is strictly validated at compile time. A missing dependency fails the build, not just the hook execution.

A practical tip: keep a checklist before deploying any module that touches hooks:

  • Clear the Symfony cache and the PrestaShop cache after registering services.
  • Verify the service tag name matches the hook name exactly.
  • Confirm the hook method signature against the documentation for version 9.
  • Run php bin/console lint:container to catch configuration errors early.
  • Test in a staging environment that mirrors your production server's PHP version.

Testing Your Overrides in PrestaShop 9: A Quick Checklist

Once you have migrated your custom database hook overrides to Symfony services, you need to verify the transition actually worked. A PHP error that only appears in production is the worst possible outcome, so run through this checklist before you deploy.

Verify the Service Is Loaded

Start by confirming your service is registered in the container. In debug mode, open the Symfony Profiler and look under the container section for your service ID. If it is missing, the services.yml file is not being loaded or the class name does not match. The PrestaShop 9 back office uses Symfony controllers throughout, so the profiler is your first stop for any service-related issue.

Check the Logs for Silent Failures

PrestaShop writes to the var/logs directory, and Symfony exceptions appear there too. Look for ServiceNotFoundException or messages about hooks not being registered. A common failure mode is a hook that was previously loaded from the database now silently returning nothing. The absence of an error does not mean the hook fired.

Simulate the Hook Without Waiting for a Customer

Use the back office to trigger the relevant page. If you overrode a display hook like displayProductExtraContent, load a product page and inspect the HTML source. For action hooks such as actionObjectProductUpdateAfter, edit a product and check that your code executed by watching the logs or a temporary debug output.

Run the Full Checklist Before Deploying

  • Confirm the service ID is registered in the Symfony container
  • Check var/logs for exceptions after triggering the hook
  • Load the relevant front-office page and verify the output in HTML source
  • Test in debug mode first, then again with debug mode disabled
  • Verify that the original database hooks are no longer firing to avoid double execution

Double execution is a subtle issue. If your old override still exists in the database and your new Symfony service also fires, you will see duplicated output or side effects. Remove the old override once the new service is confirmed working.

The quickest way to catch a broken migration is to test the service in isolation before you test the hook. If the service loads and the method runs, the remaining risk is in how the hook is registered, not in your custom code.

Database hook overrides in PrestaShop have long been the go-to method for agencies and developers to customise module behaviour without touching core files. PrestaShop 9's fully migrated Symfony core changes how hooks are registered and dispatched, which breaks many traditional database hook override patterns. This guide explains what changed, why it matters, and how to migrate your custom hooks so they keep working through the 9.x lifecycle.

Why PrestaShop 9 Breaks Traditional Hook Overrides

PrestaShop 9 represents a significant architectural shift. The PrestaShop 9.0 back office has been fully migrated to Symfony controllers and Twig templates replacing older frameworks and Smarty-based rendering. This migration touches the hook system at its core.

In versions up to 8.x, hooks were resolved primarily through database lookups. The ps_hook and ps_hook_alias tables stored hook definitions, and modules registered their hooks as database records. Custom overrides typically meant editing these records or adding new ones to change behaviour.

PrestaShop 9's Symfony core introduces a HookDispatcher that handles hook registration and execution through the Symfony event system. This changes the execution path entirely. Database records still exist for compatibility, but the dispatcher now decides what runs based on registered services and module manifests, not purely on what sits in the database.

The practical result: database edits that used to rewire hook behaviour silently stop working after the upgrade.

The 1.7.8.4 Regression: A Warning Sign

This problem did not appear overnight. Version 1.7.8.4 introduced a regression in hook retrieval that foreshadowed the current situation. The change to how hooks were retrieved from the database was coherent but overlooked a key element: modules that relied on custom database entries for hook behaviour lost functionality after the update.

Developers who maintained database hook overrides through that version already saw the writing on the wall. If a point release could break database-driven hook overrides, a full Symfony migration would dismantle them entirely.

How Hook Registration Works in PrestaShop 9

Modern PrestaShop modules declare their hooks in the module's main PHP file using the install() method with registerHook() calls. This has not changed. What changed is what happens after registration.

In PrestaShop 9, the Symfony HookDispatcher builds a runtime map of subscribers. This map is compiled from module service definitions and the hook declarations in each module. The dispatcher consults this map when an event fires, not the database tables that previous versions relied upon.

The official PrestaShop hooks documentation for versions 1.7, 8 and 9 covers action hooks, display hooks, and filter hooks with usage examples. If you compare the 8.x and 9.x documentation side by side, you will notice the 9.x entries reference Symfony event names and subscriber patterns far more explicitly.

Audit Your Existing Database Hook Overrides

Before planning a migration, you need to know what you are dealing with. Run a full audit of your current hook overrides across all modules and themes.

  • Inventory database hook records: Query ps_hook and ps_hook_alias for entries that do not correspond to a registered module hook. These are your custom overrides.
  • Check module manifests: Examine each module's config.xml and main PHP class for hooks that reference Symfony event names versus legacy names.
  • Review override folders: Look in override/classes/Hook.php and override/controllers/ for custom logic that intercepts hook execution.
  • Test after staging: Deploy a staging copy of PrestaShop 9 and run a hook-by-hook regression test before touching production.

Modules that only use standard hooks with no database customisation will generally survive the upgrade untouched. The risk concentrates in modules with hand-edited hook registrations, hook aliases, or custom dispatch logic.