Skip to content

Split monolithic designLoader plugin into design, translation and design change plugins (#29735) - #41123

Open
lbajsarowicz wants to merge 1 commit into
magento:2.4-developfrom
lbajsarowicz:fix/29735-split-design-loader-plugin
Open

Split monolithic designLoader plugin into design, translation and design change plugins (#29735)#41123
lbajsarowicz wants to merge 1 commit into
magento:2.4-developfrom
lbajsarowicz:fix/29735-split-design-loader-plugin

Conversation

@lbajsarowicz

Copy link
Copy Markdown
Contributor

Description (*)

Magento\Theme\Plugin\LoadDesignPlugin, declared as the single designLoader plugin on Magento\Framework\App\ActionInterface (frontend and adminhtml), triggers Magento\Framework\View\DesignLoader::load(), which bundles three unrelated responsibilities into one call:

$area->load(Area::PART_DESIGN);
$area->load(Area::PART_TRANSLATE);
$area->detectDesign($this->_request);

Consequence: an Action that renders no layout and no translated output — a JSON/AJAX endpoint, a lightweight custom controller — still pays for loading the full translation dictionary and for design-change detection on every request. The only escape hatch today is <plugin name="designLoader" disabled="true"/>, which also removes design initialization, so the Action loses the design it may still need (e.g. for image resizing via the theme view configuration).

This PR splits the monolith into three independently disable-able plugins, without changing any default behavior:

Plugin sortOrder Does
designLoader 10 Area::PART_DESIGN
translationLoader 20 Area::PART_TRANSLATE
designChangeLoader 30 Area::detectDesign()

A developer can now keep design initialization and opt out of just the expensive parts:

<type name="My\Module\Controller\Ajax\Index">
    <plugin name="translationLoader" disabled="true"/>
    <plugin name="designChangeLoader" disabled="true"/>
</type>

Why this split, and why the sort orders are explicit — the current execution order is load-bearing and is preserved exactly:

  1. PART_DESIGN must run first: Area::_initDesign() sets the area and the default design theme.
  2. PART_TRANSLATE must run second: Area::_initTranslate()Translate::loadData() resolves theme translation files from the design theme set in step 1.
  3. detectDesign() must run last: it may change the design (store design change or user-agent design exception) after translations were already resolved against the default theme. Moving it earlier would silently change which translation files are loaded on stores that use design changes or design exceptions.

Area::_loadPart() is idempotent (guarded by $this->_loadedParts), so splitting the calls across three plugins cannot cause double loading.

DesignLoader::load() is unchanged and still performs all three steps in the same order and with a single AreaList::getArea() call — existing callers (Magento\Backend\App\Action\Plugin\LoadDesignPlugin, Magento\Catalog\Ui\DataProvider\Product\Listing\Collector\Image) are unaffected. Three granular methods were added next to it: loadDesign(), loadTranslation(), applyDesignChange().

The LocalizedException / Magento\Framework\Config\Dom\ValidationException handling that turned an invalid design configuration into an admin error message was moved to Magento\Theme\Plugin\AbstractDesignLoaderPlugin and is shared by all three plugins, so the behavior is identical no matter which part fails. LoadDesignPlugin's public constructor signature is unchanged.

Scope note: the designLoader plugin on ActionInterface is declared only in Theme/etc/frontend/di.xml and Theme/etc/adminhtml/di.xml. webapi_rest, webapi_soap and graphql are out of scope — the plugin was never declared there (GraphQL has its own Magento\CatalogGraphQl\Plugin\DesignLoader).

Fixed Issues (if relevant)

  1. Fixes The designLoader plugin loads too much data Magento\Theme\Plugin\LoadDesignPlugin #29735: Design loader plugin adds overhead to every controller

Manual testing scenarios (*)

  1. Install a vanilla instance, open a frontend category page and a product page — design, translations and the store's design change apply exactly as before.
  2. Configure a Design Change for the store (Content → Design → Schedule) and confirm the scheduled theme is applied on the frontend.
  3. Configure a User Agent Exception for a theme (Content → Design → Configuration → Other Settings → Search Engine Optimization / Design Exception) and confirm the exception theme is applied for the matching user agent.
  4. Open the admin panel — the admin theme and admin translations render as before.
  5. Break the design configuration so it raises a ValidationException and confirm the admin still renders with the error message added, instead of a fatal error.
  6. Declare a custom controller with translationLoader and designChangeLoader disabled, and confirm it executes without loading translations (verify via Magento\Framework\TranslateInterface not being populated, or via profiler load_area:frontend.translate disappearing from the profile) while design-dependent code still works.

Contribution checklist (*)

  • Pull request has a meaningful description of its purpose
  • All commits are accompanied by meaningful commit messages
  • All new or changed code is covered with unit/integration tests (if applicable)
  • All automated tests passed successfully (all builds are green)

…ign change plugins (magento#29735)

DesignLoader::load() bundled loading of the design part, the translation part
and the store design change behind a single designLoader plugin, so an Action
that renders no layout and no translated output could not opt out of the
expensive parts without losing design initialization as well.

The plugin is now three plugins with explicit sort orders that preserve the
existing execution order, and DesignLoader::load() keeps its current behavior
for direct callers.

Fixes magento#29735
@m2-assistant

m2-assistant Bot commented Aug 13, 2026

Copy link
Copy Markdown

Hi @lbajsarowicz. Thank you for your contribution!
Here are some useful tips on how you can test your changes using Magento test environment.
❗ Automated tests can be triggered manually with an appropriate comment:

  • @magento run all tests - run or re-run all required tests against the PR changes
  • @magento run <test-build(s)> - run or re-run specific test build(s)
    For example: @magento run Unit Tests

<test-build(s)> is a comma-separated list of build names.

Allowed build names are:
  1. Database Compare
  2. Functional Tests CE
  3. Functional Tests EE
  4. Functional Tests B2B
  5. Integration Tests
  6. Magento Health Index
  7. Sample Data Tests CE
  8. Sample Data Tests EE
  9. Sample Data Tests B2B
  10. Static Tests
  11. Unit Tests
  12. WebAPI Tests
  13. Semantic Version Checker

You can find more information about the builds here
ℹ️ Run only required test builds during development. Run all test builds before sending your pull request for review.


For more details, review the Code Contributions documentation.
Join Magento Community Engineering Slack and ask your questions in #github channel.

@lbajsarowicz

Copy link
Copy Markdown
Contributor Author

@magento run all tests

@engcom-Bravo engcom-Bravo added the Priority: P2 A defect with this priority could have functionality issues which are not to expectations. label Aug 14, 2026
@github-project-automation github-project-automation Bot moved this to Pending Review in Pull Requests Dashboard Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Priority: P2 A defect with this priority could have functionality issues which are not to expectations. Progress: pending review

Projects

Status: Pending Review

Development

Successfully merging this pull request may close these issues.

The designLoader plugin loads too much data Magento\Theme\Plugin\LoadDesignPlugin

2 participants