Using modern pages, you will have access to the PrestaShop debug toolbar, the service container, Twig and Doctrine, among others. For your views, the PrestaShop UI Kit is available, built on top of Bootstrap 4 and ensuring your views are consistent with the PrestaShop Back Office.
FrameworkBundleAdminController is deprecated and will be removed in PrestaShop 10.0. Use PrestaShopAdminController instead for new controllers.In your module, create a new controller class in the src/Controller/ directory:
<?php
// modules/your-module/src/Controller/DemoController.php
namespace MyModule\Controller;
use PrestaShopBundle\Controller\Admin\PrestaShopAdminController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class DemoController extends PrestaShopAdminController
{
/**
* Inject services via constructor for services used across multiple actions
*/
public function __construct(
private readonly MyCustomService $myCustomService,
) {
}
/**
* You can also inject services directly in action methods
*/
public function demoAction(AnotherService $anotherService): Response
{
// Use injected services
$data = $this->myCustomService->getData();
$moreData = $anotherService->getMoreData();
return $this->render('@Modules/your-module/templates/admin/demo.html.twig', [
'data' => $data,
'moreData' => $moreData,
]);
}
/**
* You can also use the #[Autowire] attribute to inject services by their ID
*/
public function advancedAction(
Request $request,
ShopRepository $shopRepository,
#[Autowire(service: 'my_module.custom_service')] $customService,
): Response
{
// Use the injected service
$customService->execute();
return $this->render('@Modules/your-module/templates/admin/advanced.html.twig');
}
/**
* Use helper methods provided by PrestaShopAdminController
*/
public function anotherAction(): Response
{
// PrestaShopAdminController provides helper methods for common services
$config = $this->getConfiguration()->get('my_config');
return $this->render('@Modules/your-module/templates/admin/another.html.twig',[
'my_config' => $config,
]);
}
}
The example above demonstrates a complete controller implementation with:
MyCustomService)AnotherService)#[Autowire(service: 'my_module.custom_service')])PrestaShopAdminController for accessing common PrestaShop servicesYou have access to Twig as rendering engine and everything from the Symfony framework ecosystem.
Note that you must return a Response object, but this can be a JsonResponse if you plan to make a single page application (or “SPA”).
Accessing PrestaShop services:
The PrestaShopAdminController provides helper methods to access common PrestaShop services:
$this->getConfiguration() - Configuration service$this->getTranslator() or $this->trans() - Translation service$this->getRouter() - Router service$this->getEmployeeContext() - Employee context$this->getShopContext() - Shop context$this->getLanguageContext() - Language context$this->getCurrencyContext() - Currency context$this->getCountryContext() - Country context$this->dispatchCommand() - Execute CQRS commands$this->dispatchQuery() - Execute CQRS queries$this->dispatchHookWithParameters() - Dispatch hooks$this->presentGrid() - Present grid dataSee the complete list in the PrestaShopAdminController source code.
Doctrine repositories must be injected:
Doctrine ORM is not directly accessible via $this->getDoctrine() or similar methods. All Doctrine repositories and entity managers must be injected as dependencies in your controller’s constructor or action methods.
Example:
use Doctrine\ORM\EntityManagerInterface;
use App\Repository\ProductRepository;
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly ProductRepository $productRepository,
) {
}
In PrestaShop 9.0, controllers must be defined as services. You have two main approaches to configure your controller service:
# modules/your-module/config/services.yml
services:
MyModule\Controller\DemoController:
autowire: true
autoconfigure: true
tags:
- { name: controller.service_arguments }
# modules/your-module/config/services.yml
services:
_defaults:
public: false
autowire: true
autoconfigure: true
MyModule\Controller\DemoController: ~
Service configuration is mandatory:
One of the two service configuration options above is essential and required for your controller to work properly. Without this configuration:
PrestaShopAdminController will not be accessibleImportant: The service name (e.g., MyModule\Controller\DemoController) must exactly match the fully qualified class name (FQCN) of your controller. If the service name doesn’t match the class name, Symfony and PrestaShop will not be able to identify and instantiate the controller, resulting in errors.
Understanding the configuration:
autowire: true - Automatically injects services in constructors and method parametersautoconfigure: true - Automatically configures the controller as a service and enables all controller featurescontroller.service_arguments tag - Required if your controller doesn’t extend AbstractController to enable method parameter injectionYou must enable the autoloading for this Controller. For example using a composer.json file for your module.
Use namespace for your Controller file
<?php
// modules/your-module/src/Controller/DemoController.php
namespace MyModule\Controller;
use PrestaShopBundle\Controller\Admin\PrestaShopAdminController;
Configure composer to autoload this namespace
{
"name": "you/your-module",
"description": "...",
"autoload": {
"psr-4": {
"MyModule\\": "src/"
}
},
"config": {
"prepend-autoloader": false
},
"type": "prestashop-module"
}
You should run composer dumpautoload console command from where your module’s composer.json file is located.
If you do not have “composer” you can search for it. Composer is available on any operating system.
Now we have created and loaded your controller, you need to declare a route. A route map an action of your controller to an URI.
This is really simple (and very well documented in Symfony’s Routing component documentation):
For instance:
# modules/your-module/config/routes.yml
your_route_name:
path: your-module/demo
methods: [GET]
defaults:
_controller: 'MyModule\Controller\DemoController::demoAction'
_controller attribute, you don’t even need to create your own controller! You could even use a public function from your module main class. Even so, we strongly suggest using a controller.::) and not the single colon (:)
to separate classes and method names!
Since Symfony 4.1 the bundle notation is going to be deprecated: https://symfony.com/blog/new-in-symfony-4-1-deprecated-the-bundle-notationThe Controller in the previous example will now be available if you browse /admin-dev/modules/your-module/demo. Pay attention to this path: it starts with /modules.
This is because all module routes are, by default, prefixed with /modules.
If however you need or wish your route not to be prefixed, you can use the _disable_module_prefix route option to disable the prefix.
# modules/your-module/config/routes.yml
your_route_name:
path: your-module/demo
methods: [GET]
defaults:
_controller: 'MyModule\Controller\DemoController::demoAction'
_disable_module_prefix: true
Valid URIs required a security token.
In order to generate the valid URI of a controller you created from inside the main module class, you need to get the Symfony router. The router will build the URI using generate with the route name, as in the below example.
<?php
# modules/my-module/my-module.php
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
class MyModule extends Module
{
protected function generateControllerURI()
{
$router = SymfonyContainer::getInstance()->get('router');
return $router->generate('my_route_name');
}
}
In Symfony 6.4, controllers must be defined as services. The container passed to controllers is no longer the “global container” but a dedicated, optimized container based on the services injected into it.
Important implications:
$this->get('service_name') method is no longer available in modern controllersFrameworkBundleAdminController maintains backward compatibility but is deprecated and will be removed in PrestaShop 10.0PrestaShopAdminController which follows Symfony best practicesYou have three main ways to inject services:
#[Autowire(service: 'service_id')] to inject services by their IDThe new base controller provides convenient helper methods for commonly used services:
$this->getConfiguration() - Access configuration service$this->getTranslator() - Access translator service$this->getRouter() - Access router service$this->getFlashBag() - Access flash messagesSee the full list of helper methods in the class.
Learn more about Symfony controllers:
It is safer to define permissions required to use your controller, this can be configured using the #[AdminSecurity] attribute and some configuration in your routing file. You can read this documentation if you need more details about Controller Security.
@AdminSecurity annotation has been replaced with the #[AdminSecurity] attribute following PHP 8 standards.