From b252858177c4689eb9d624e1d3459834cda0b371 Mon Sep 17 00:00:00 2001 From: luigi massa Date: Thu, 23 Oct 2025 12:16:38 +0200 Subject: [PATCH] Module AdminControllers for back office documentation has to be adjusted for PS9 --- .../controllers/admin-controllers/_index.md | 230 ++++++++++++++---- 1 file changed, 188 insertions(+), 42 deletions(-) diff --git a/modules/concepts/controllers/admin-controllers/_index.md b/modules/concepts/controllers/admin-controllers/_index.md index a9c2002b77..4ba6b281fb 100644 --- a/modules/concepts/controllers/admin-controllers/_index.md +++ b/modules/concepts/controllers/admin-controllers/_index.md @@ -9,75 +9,179 @@ Using modern pages, you will have access to the PrestaShop debug toolbar, the se ## How to declare a new Controller -Somewhere in your module declare a new class that will act as a Controller: +{{% notice warning %}} +**PrestaShop 9.0+**: The `FrameworkBundleAdminController` is **deprecated** and will be removed in PrestaShop 10.0. Use `PrestaShopAdminController` instead for new controllers. +{{% /notice %}} + +In your module, create a new controller class in the `src/Controller/` directory: + ```php cache = $cache; + // 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, + ]); } - public function demoAction() + /** + * 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 { - return $this->render('@Modules/your-module/templates/admin/demo.html.twig'); + // 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: +- **Constructor injection** for services used across multiple actions (`MyCustomService`) +- **Method parameter injection** for services used in specific actions (`AnotherService`) +- **Autowire attribute** for injecting services by their ID (`#[Autowire(service: 'my_module.custom_service')]`) +- **Helper methods** provided by `PrestaShopAdminController` for accessing common PrestaShop services + +You 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"). + +{{% notice note %}} +This controller works exactly the same as the Core Back Office ones. +{{% /notice %}} + +{{% notice info %}} +**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 data + +See the complete list in the [PrestaShopAdminController source code](https://github.com/PrestaShop/PrestaShop/blob/9.0.0/src/PrestaShopBundle/Controller/Admin/PrestaShopAdminController.php). +{{% /notice %}} + +{{% notice warning %}} +**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: +```php +use Doctrine\ORM\EntityManagerInterface; +use App\Repository\ProductRepository; + +public function __construct( + private readonly EntityManagerInterface $entityManager, + private readonly ProductRepository $productRepository, +) { } ``` +{{% /notice %}} + +## Service Configuration + +In PrestaShop 9.0, controllers must be defined as services. You have two main approaches to configure your controller service: + +### Option 1: Explicit service configuration with tags -If you want Symfony Dependency Injection to inject services into your controller, you need to use specific YAML service declaration: ```yaml +# modules/your-module/config/services.yml services: - # The name of the service must match the full namespace class MyModule\Controller\DemoController: - class: MyModule\Controller\DemoController - arguments: - - '@doctrine.cache.provider' + autowire: true + autoconfigure: true + tags: + - { name: controller.service_arguments } ``` -You can also retrieve services with the container available in symfony controllers -> -```php -container->get('doctrine.cache'); - - return $this->render('@Modules/your-module/templates/admin/demo.html.twig'); - } -} -``` +One of the two service configuration options above is **essential and required** for your controller to work properly. Without this configuration: +- **The controller will not function** and will throw errors +- Helper methods from `PrestaShopAdminController` will not be accessible +- Services like Twig, Form, Router, and other Symfony components will not be available -You have access to the Container, to Twig as rendering engine, the Doctrine ORM, everything from 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"). +**Important:** 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. -{{% notice note %}} -This controller works exactly the same as the Core Back Office ones. -{{% /notice %}} +**Understanding the configuration:** +- `autowire: true` - Automatically injects services in constructors and method parameters +- `autoconfigure: true` - Automatically configures the controller as a service and enables all controller features +- `controller.service_arguments` tag - Required if your controller doesn't extend `AbstractController` to enable method parameter injection +{{% /notice %}} + +## Autoloading Configuration You must enable the autoloading for this Controller. For example using a `composer.json` file for your module. -#### Example using PSR-4 namespacing +### Example using PSR-4 namespacing 1. Use namespace for your Controller file @@ -87,7 +191,7 @@ You must enable the autoloading for this Controller. For example using a `compos namespace MyModule\Controller; - use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController; + use PrestaShopBundle\Controller\Admin\PrestaShopAdminController; ``` 2. Configure composer to autoload this namespace @@ -98,7 +202,7 @@ You must enable the autoloading for this Controller. For example using a `compos "description": "...", "autoload": { "psr-4": { - "MyModule\\Controller\\": "src/Controller/" + "MyModule\\": "src/" } }, "config": { @@ -182,6 +286,48 @@ In order to generate the valid URI of a controller you created from inside the m ``` +## Key Changes in PrestaShop 9.0 (Symfony 6.4) + +### Controllers as Services + +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:** + +- The `$this->get('service_name')` method is no longer available in modern controllers +- `FrameworkBundleAdminController` maintains backward compatibility but is **deprecated** and will be removed in PrestaShop 10.0 +- New controllers should extend `PrestaShopAdminController` which follows Symfony best practices + +### Dependency Injection Methods + +You have three main ways to inject services: + +1. **Constructor injection** - For services used across multiple actions +2. **Method injection** - For services used in a single action (more optimized) +3. **Autowire attribute** - Use `#[Autowire(service: 'service_id')]` to inject services by their ID + +### PrestaShopAdminController Helper Methods + +The 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 messages + +See the [full list of helper methods](https://github.com/PrestaShop/PrestaShop/blob/9.0.0/src/PrestaShopBundle/Controller/Admin/PrestaShopAdminController.php) in the class. + +{{% notice info %}} +**Learn more about Symfony controllers:** +- [Controllers as services](https://symfony.com/doc/6.4/controller/service.html) +- [Service Subscribers & Locators](https://symfony.com/doc/6.4/service_container/service_subscribers_locators.html) +- [Service container](https://symfony.com/doc/6.4/service_container.html) +{{% /notice %}} + ## Secure your controller 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]({{< ref "/9/development/architecture/migration-guide/controller-routing.md#security" >}}). + +{{% notice note %}} +**PrestaShop 9.0+**: The `@AdminSecurity` annotation has been replaced with the `#[AdminSecurity]` attribute following PHP 8 standards. +{{% /notice %}}