Blog navigation

Blog Rss rss_feed

Symfony Controllers in PrestaShop 9: Modern Back Office Module Development

Symfony Controllers in PrestaShop 9: Modern Back Office Module Development

When a PrestaShop module needs its own administration page, developers usually have two possible approaches: legacy ModuleAdminController pages or modern Symfony-based controllers.

For new Back Office development in PrestaShop 9, Symfony controllers are generally the better direction.

They integrate naturally with Twig, Dependency Injection, Symfony routing and modern PrestaShop services.

This article explains the basic structure of a modern Back Office controller and the main differences compared with the legacy approach.

Where should a modern controller be located?

Legacy admin controllers are usually stored in:

controllers/admin/

Modern Symfony controllers belong in:

src/Controller/

A simple module structure can look like this:

mymodule/
├── config/
│   ├── routes.yml
│   └── services.yml
├── src/
│   └── Controller/
│       └── ImportController.php
├── views/
│   └── templates/
│       └── admin/
│           └── import.html.twig
└── mymodule.php

Keeping Symfony controllers under src/Controller makes the module structure clearer and easier to maintain.

Which base controller should be used?

Older PrestaShop examples often use:

FrameworkBundleAdminController

For new code in PrestaShop 9, the more appropriate base class is:

PrestaShopAdminController

A minimal controller can look like this:

<?php

namespace Ewonta\MyModule\Controller;

use PrestaShopBundle\Controller\Admin\PrestaShopAdminController;
use Symfony\Component\HttpFoundation\Response;

final class ImportController extends PrestaShopAdminController
{
    public function indexAction(): Response
    {
        return $this->render(
            '@Modules/mymodule/views/templates/admin/import.html.twig'
        );
    }
}

This gives the controller access to modern Symfony and PrestaShop functionality while keeping the implementation compatible with current Back Office architecture.

Add a route for the controller

Creating the controller class is not enough.

Symfony also needs to know which URL should call it.

Create:

config/routes.yml

and define a route:

mymodule_import:
  path: mymodule/import
  methods: [GET]
  defaults:
    _controller: 'Ewonta\MyModule\Controller\ImportController::indexAction'

Using Symfony routing is preferable to manually building admin URLs.

It keeps route generation centralised and easier to change later.

Controllers are services in modern PrestaShop

This is one of the most important architectural changes.

Modern controllers are handled as Symfony services.

For example:

services:
  _defaults:
    autowire: true
    autoconfigure: true

  Ewonta\MyModule\Controller\ImportController: ~

This means dependencies can be injected directly into the controller.

For example:

final class ImportController extends PrestaShopAdminController
{
    public function __construct(
        private readonly ProductImporter $productImporter
    ) {
    }

    public function indexAction(): Response
    {
        $this->productImporter->run();

        return $this->render(
            '@Modules/mymodule/views/templates/admin/import.html.twig'
        );
    }
}

The controller no longer needs to manually create the importer.

Symfony resolves the dependency through the service container.

Avoid business logic inside controllers

A common mistake is to move all module logic into the controller.

For example:

public function importAction(): Response
{
    // Open XML
    // Validate products
    // Update prices
    // Download images
    // Update stock
    // Write logs
}

This quickly creates a controller that is difficult to maintain.

A better structure is:

HTTP Request
     ↓
Controller
     ↓
ProductImporter
     ↓
Repository / API / Services
     ↓
Response

The controller should coordinate the request and response.

The actual business logic should live in dedicated services, Commands or Handlers.

Use Twig instead of HTML inside PHP

Modern Back Office pages should normally use Twig templates.

Example:

{% extends '@PrestaShop/Admin/layout.html.twig' %}

{% block content %}
  <div class="card">
    <div class="card-header">
      Product Import
    </div>

    <div class="card-body">
      ...
    </div>
  </div>
{% endblock %}

The controller only passes the necessary data:

return $this->render(
    '@Modules/mymodule/views/templates/admin/import.html.twig',
    [
        'productsCount' => $productsCount,
    ]
);

This creates a cleaner separation:

Controller → logic
Twig       → presentation

It also makes the admin interface easier to redesign without changing application logic.

PrestaShopAdminController already provides useful tools

A modern admin controller does not need to implement every basic PrestaShop feature manually.

It gives access to common functionality such as:

  • configuration;

  • translations;

  • routing;

  • PrestaShop context;

  • Commands and Queries.

For example:

$value = $this->getConfiguration()->get(
    'MYMODULE_OPTION'
);

Translations can also be handled directly:

$message = $this->trans(
    'Import completed successfully.',
    [],
    'Modules.Mymodule.Admin'
);

This allows the controller to stay focused on application flow rather than infrastructure.

Symfony controllers work well with CQRS

Modern controllers fit naturally with CQRS architecture.

For example:

Controller
    ↓
Command
    ↓
Command Handler
    ↓
Service
    ↓
Repository / ObjectModel / API

A controller can send a Command instead of modifying products or orders directly.

This keeps the HTTP layer separated from business operations.

It is especially useful for modules that also expose the same operation through:

CLI
Cron
API
Back Office

Each entry point can reuse the same Command or service.

Do not forget permissions

An administration page is not automatically safe just because it is hidden behind the Back Office.

PrestaShop has its own employee permission system.

Administrative actions such as:

changing products
updating prices
running imports
managing orders
editing integrations

should be protected with appropriate access rules.

Modern controllers can use the AdminSecurity attribute for this purpose.

Permissions are especially important when a shop has multiple employees with different roles.

Adding the page to the Back Office menu

If the page should appear in the left-side Back Office menu, defining a route is not enough.

PrestaShop still uses the Tab system to connect admin pages with navigation and permissions.

A modern Symfony page can therefore be linked to:

$tabs

in the main module class.

The route can also use legacy compatibility information where necessary, allowing Symfony pages to integrate with the existing Back Office permission system.

This is a good example of how modern and legacy parts of PrestaShop continue to coexist.

Legacy or Symfony controller?

Legacy controllers have not disappeared completely.

Front Office module pages still commonly rely on:

ModuleFrontController

So a real module may use both approaches:

Front Office
    ↓
ModuleFrontController

Back Office
    ↓
Symfony Controller
    ↓
PrestaShopAdminController

This is normal in current PrestaShop development.

The platform is still transitioning gradually rather than replacing every legacy component at once.

Recommended module structure

For a medium or large module, a clean structure might look like this:

mymodule/
├── config/
│   ├── routes.yml
│   └── services.yml
├── src/
│   ├── Controller/
│   ├── Service/
│   ├── Command/
│   ├── CommandHandler/
│   ├── Query/
│   └── QueryHandler/
├── views/
│   └── templates/
│       └── admin/
└── mymodule.php

This makes responsibilities easier to understand.

For example:

Controller      → HTTP request
Command         → requested action
Handler         → operation coordinator
Service         → business logic
Twig            → presentation

Conclusion

For new Back Office development in PrestaShop 9, Symfony controllers provide a cleaner and more flexible architecture than relying entirely on legacy admin controllers.

The basic structure is straightforward:

src/Controller/
      ↓
PrestaShopAdminController
      ↓
config/routes.yml
      ↓
Symfony Service
      ↓
Twig

The most important rule is to keep business logic outside the controller.

Use services, Commands and Handlers for actual operations, while the controller remains responsible for request handling and response generation.

This approach works especially well for larger PrestaShop modules with complex Back Office interfaces, imports, APIs, cron jobs or CLI commands.

It also prepares the codebase for future PrestaShop versions where Symfony-based architecture will continue to play a larger role.

Official documentation: Admin controllers — PrestaShop Developer Documentation

Was this blog post helpful to you?

    
👈 Присоединяйтесь к нашему Telegram-каналу!

Будьте в курсе последних новинок и фишек e-commerce: советы, полезные инструменты и эксклюзивные материалы.

No comments at this moment
close

Checkout

close

Favourites

Promo