Skip to content

Commit

Permalink
Merge pull request #1829 from jolelievre/symfony-layout
Browse files Browse the repository at this point in the history
Detail the impacts of the new Symfony layout
  • Loading branch information
kpodemski authored Jun 26, 2024
2 parents 84bab7d + 26c9775 commit 2bda745
Showing 1 changed file with 91 additions and 9 deletions.
100 changes: 91 additions & 9 deletions modules/core-updates/9.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,97 @@ Display event listeners for the back office in `dev` environment (default value
- [Experimental front container](https://github.com/PrestaShop/PrestaShop/pull/32719)
- [Admin API has dedicated kernel, we now have three distinct configurations](https://github.com/PrestaShop/PrestaShop/pull/35515)

### Symfony layout

All the back office pages share a common layout, composed of a few elements:
- the `<head>` element that includes all the CSS and JS (among other things)
- the side navigation menu
- the header of the page which is itself composed of:
- quick accesses
- search form
- notifications center
- employee dropdown
- the multistore header (when multistore is enabled)
- the page toolbar (which includes breadcrumb and top action buttons)
- the footer (which only contains a displayBackOfficeFooter display hook)

Until PrestaShop 8.1, all these common elements were handled by legacy code, so even on the migrated pages, there was always a background legacy controller based on `AdminController` in charge of building the layout data and rendering it. Once the layout was rendered the central content of Symfony pages was included in the middle of it. It means no page was completely free from the legacy controllers, which would ultimately block the end of migration, while they are intended to disappear completely.

In PrestaShop 9.0 all this layout part is now fully handled by Symfony, we use <a href="https://symfony.com/bundles/ux-twig-component/current/index.html" target="_blank">Twig components</a> to render each element independently. The code is, therefore, easier to understand, a component class is responsible for fetching/building the data, while the actual rendering is based on Twig. See our <a href="https://github.com/PrestaShop/PrestaShop/tree/develop/src/PrestaShopBundle/Twig/Component" target="_blank">Layout components</a> for more details.

On legacy pages, we follow the same principle. Symfony is now in charge of rendering all the layout, and we use some <a href="https://github.com/PrestaShop/PrestaShop/tree/develop/src/PrestaShopBundle/Twig/Component/Legacy" target="_blank">Legacy layout components</a> that follow the same architecture, but render different Twig templates to fit with the old **default theme** (that is based on old Bootstrap and includes old legacy helpers like `HelperForm` and `HelperList`).

**What changes for my module pages?**

We refactored this layout with maximum backward compatibility in mind. The HTML layout itself had minimum changes (in both migrated and legacy pages). We tried to keep the same hooks in both the PHP code and the Twig templates. We even introduced a fake legacy controller in migrated pages that has no logic but is kept mostly as a [DTO](https://en.wikipedia.org/wiki/Data_transfer_object) to contain things like CSS files and JS files, because that's where most modules add their content.
We still had to change the controller workflow. Many of the functions in `AdminController` are no longer useful as they are used to initialize the layout variables (now handled by Twig components). Some methods also render or write output directly, which we prevent as we need to get the rendered content as a string to integrate it correctly inside the new layout. This means some internal methods had to be split to avoid unwanted usage.

**Legacy workflow:**

- `Dispatcher::dispatch` would detect the legacy controller and instantiate it
- `AdminController::run` was executed by the `Dispatcher`, which was split into
- `AdminController::init`
- `AdminController::checkAccess` would handle the check of the legacy token in the url
- `AdminController::setMedia` is in charge of defining the JS/CSS files to be loaded in the page
- `AdminController::postProcess` processes the posted data
- if after this method `AdminController::redirect_after` is defined then perform an HTTP redirection
- `AdminController::initHeader`
- `AdminController::initContent`
- `AdminController::initFooter`
- Check if `AdminController::ajax`, one of the two is executed
- `AdminController::displayAjax` (or specific `displayAjax{action}` method)
- `AdminController::display`
- init many variables used by the layout templates (meta title, toolbar, JS definitions, ...)
- render `page`, `header`, `footer` and sets them as Smarty variables
- fetch the Smarty `AdminController::template` to get the content of the page
- calls `smartyOutputContent` to echo/output the whole page (layout + content)

Here you'll find the [execution workflow of legacy controllers]({{< relref "9/development/architecture/legacy/legacy-controllers.md" >}})

**Symfony workflow:**

- A special routing matching URLs like `?controller=LegacyAdminController` checks if the URL is valid thanks to `LegacyRouterChecker::check`
- the query parameter must match an existing legacy controller class
- the controller object is instantiated and `AdminController::init` is called, this is important to know if the controller is anonymous, this is the proper moment to define `AdminController::allowAnonymous`
- a few attributes are set on the Symfony `Request` so they can be reused later in the workflow by other components/services
- `LegacyController::legacyPageAction` is a generic controller responsible for executing the legacy controller, it doesn't execute `AdminController::run` but splits it into several steps
- `AdminController::checkAccess` is not called, the token in URLs is a Symfony CSRF token now and is checked by `TokenizedUrlListener`
- `LegacyController::initController`
- the permission is checked based on the detected controller and action, unless `AdminController::viewAccess` has been overridden in which case it is used to check authorization
- `SmartyVariablesFiller::fillDefault` is in charge of building most of the generic smarty variables previously handled by `AdminController::initHeader`
- `AdminController::setMedia` is in charge of defining the JS/CSS files to be loaded in the page
- `AdminController::postProcess` processes the posted data
- if after this method `AdminController::redirect_after` is defined then perform an HTTP redirection
- Check if `ajax` query parameter is defined, one of the two is executed
- `LegacyController::renderAjaxController` executes `AdminController::initContent` the appropriate `AdminController::displayAjax` method, it catches all the output data into a string buffer (because most legacy ajax methods simply echo their content) and returns it as a response
- `LegacyController::renderPageContent`
- executes `AdminController::initContent`
- fetch the Smarty `AdminController::template` to get the content of the page
- calls `Cookie::write` which was previously called via `smartyOutputContent`
- get the modal content via `AdminController::renderModal`

The two workflows should render the same result. Many methods from the legacy workflow are not executed anymore because they lost their purpose, but in case you override one of those methods, be aware that they are no longer called directly, so there are breaking changes:
- `AdminController:run`
- `AdminController::initHeader`
- `AdminController::initContent`
- `AdminController::initFooter`
- `AdminController:display`

Also, be aware that many Smarty variables are no longer defined because they were only used to render the layout.

Despite the changes in the workflow and the fact we no longer depend on the `Dispatcher` class, we **maintained** these hooks:
- `actionDispatcher`
- `actionDispatcherBefore`
- `actionDispatcherAfter`

**Related PRs:**
- [Smarty variables viewport_scale and inline_js variables no longer present](https://github.com/PrestaShop/PrestaShop/pull/33775)
- [Removed modal_module_list, modals views in new layout](https://github.com/PrestaShop/PrestaShop/pull/33864)
- [Clean Smarty variables that were only relevant for the layout internal use](https://github.com/PrestaShop/PrestaShop/pull/34560)
- [Legacy pages are rendered by Symfony](https://github.com/PrestaShop/PrestaShop/pull/34783)

For more details about the changes, you can check the content of the <a href="https://github.com/PrestaShop/PrestaShop/issues/32875" target="_blank">Symfony layout Epic</a>.

### BO login and authorization migrated to Symfony

The back office login page has been migrated to Symfony. Along with this change, the authorization system in the back office is now also based on Symfony, which implies several things:
Expand Down Expand Up @@ -328,15 +419,6 @@ This changes the data passed to the smarty templates:
- [Type hint front controllers](https://github.com/PrestaShop/PrestaShop/pull/32846)
- [Strong types in legacy admin controllers](https://github.com/PrestaShop/PrestaShop/pull/34653)

### Symfony layout

@todo explain the new workflow in BO the responsibility of twig components, the difference in how a legacy controller is executed and rendered

- [Smarty variables viewport_scale and inline_js variables no longer present](https://github.com/PrestaShop/PrestaShop/pull/33775)
- [Removed modal_module_list, modals views in new layout](https://github.com/PrestaShop/PrestaShop/pull/33864)
- [Clean Smarty variables that were only relevant for the layout internal use](https://github.com/PrestaShop/PrestaShop/pull/34560)
- [Legacy pages are rendered by Symfony](https://github.com/PrestaShop/PrestaShop/pull/34783)

### Change of behaviour/rules or removed code

- [Customization quantity feature has been removed](https://github.com/PrestaShop/PrestaShop/pull/12422), the customization quantity is now the one from the cart_product row
Expand Down

0 comments on commit 2bda745

Please sign in to comment.