From 5fe9e1f75bee20c9024b4918fc8a86889d697c2c Mon Sep 17 00:00:00 2001 From: somethings Date: Fri, 21 Aug 2026 15:38:54 +0200 Subject: [PATCH 01/26] docs: add plugin scaffolding troubleshooting guide --- ...affolding-and-generator-troubleshooting.md | 241 ++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 guides/plugins/plugins/scaffolding-and-generator-troubleshooting.md diff --git a/guides/plugins/plugins/scaffolding-and-generator-troubleshooting.md b/guides/plugins/plugins/scaffolding-and-generator-troubleshooting.md new file mode 100644 index 0000000000..95b275bdbc --- /dev/null +++ b/guides/plugins/plugins/scaffolding-and-generator-troubleshooting.md @@ -0,0 +1,241 @@ +--- +nav: + title: Scaffolding and generator troubleshooting + position: 25 + +--- + +# Scaffolding and generator troubleshooting + +Shopware's plugin scaffolding is designed to give developers a useful starting point, but generated files are often part of a larger feature. A PHP class, JavaScript module, configuration entry, route import, or service definition may each be valid on its own while the feature still is not discoverable or usable. + +This guide explains the mental model behind scaffolding and gives a troubleshooting path that works well both for humans and for coding assistants. + +::: info +This guide describes the current Core `bin/console plugin:create` scaffolding workflow and the conventions it generates. The newer `shopware-cli extension create ...` generator work is being developed separately in the Shopware CLI repository. See [#1255](https://github.com/shopware/shopware-cli/issues/1255) and [#1280](https://github.com/shopware/shopware-cli/issues/1280). +::: + +## Start with the smallest scaffold + +If you are not sure which example components you need, prefer a minimal plugin scaffold and add features when you need them. The current Core `plugin:create` command supports both a minimal skeleton and optional example components. + +```bash +bin/console plugin:create SwagBasicExample --no-scaffold +``` + +The interactive form asks whether optional files should be scaffolded. The optional generators are examples: they are useful for learning conventions, but you should review the generated files rather than assuming that selecting a generator creates your final production implementation. + +For the current Core scaffold, examples include a command, scheduled task, event subscriber, Storefront controller, Store API route, Administration module, Storefront JavaScript plugin, custom field set, and entities. The exact generated files depend on the component and your Shopware version. See the [Creating Plugins](./creating-plugins.md) guide for the current command options. + +## The important mental model: a feature is usually a set of coordinated pieces + +When a generated feature does not work, ask which of these layers are involved: + +1. **Code** — Is the class/component present, with the right namespace and path? +2. **Registration** — Does the service container know about it? +3. **Discovery** — Is the route, command, module, task, or test imported/registered? +4. **Build/cache** — Has the relevant cache or Administration build been refreshed? +5. **Runtime verification** — Can you actually call/open/run the feature? + +A common mistake is stopping after layer 1 or layer 3. For example, a storefront controller can appear in `debug:router` and still fail at request time if its service registration is incomplete. + +## Why namespace and autoloading matter + +Plugin Composer autoloading commonly maps the plugin namespace prefix to `src/`: + +```json +"autoload": { + "psr-4": { + "Swag\\BasicExample\\": "src/" + } +} +``` + +A file at: + +```text +src/Subscriber/OrderPlacedSubscriber.php +``` + +therefore needs a matching namespace such as: + +```php +namespace Swag\BasicExample\Subscriber; +``` + +When a class is not found, compare these three values first: + +```text +composer.json PSR-4 prefix + ↓ +PHP namespace + ↓ +filesystem path +``` + +They must describe the same class. + +## Service registration: what the tags mean + +Many Shopware features are Symfony services. The service container needs to know how a class should participate in the framework. + +For example, an event subscriber can use: + +```xml + + + +``` + +The PHP class declares which events it subscribes to. The `kernel.event_subscriber` tag tells Symfony to treat the service as an event subscriber and register it with the event dispatcher. + +Likewise, a console command is registered with the `console.command` tag, while a scheduled task uses the `shopware.scheduled.task` tag. + +The current Core scaffolding generators append service definitions to `src/Resources/config/services.php` for these features rather than asking you to remember every registration detail. + +::: warning +Current Core code also supports legacy XML service configuration in existing plugins, but `services.xml` is deprecated. When you touch an older plugin, check whether the current version expects `services.php` or `services.yaml` before copying an example blindly. +::: + +## Routes: discovery is not the same as reachability + +A route-based feature normally has at least two stages of verification: + +```bash +bin/console debug:router | grep my-route +``` + +proves that the route is in the router. It does **not** prove that the request will execute successfully. + +For a Storefront controller, verify the actual URL as well. If the route is visible but the request returns a server error, inspect the controller's service definition and route configuration. + +For Store API routes, a request may reach Shopware but return `401 Unauthorized` because Store API authentication is required. A 401 from the authentication layer is different from a 404 caused by an undiscovered route. + +## Commands: use `bin/console` as the verification target + +For a generated console command, use both: + +```bash +bin/console list | grep my-prefix +``` + +and then run the command itself: + +```bash +bin/console my-prefix:my-command +``` + +The first verifies discovery. The second verifies that the command can actually execute. + +The same principle applies to other generators: prefer a verification command or a real request over a file listing. + +## Scheduled tasks: verify the task lifecycle + +A scheduled task is more than a PHP class. The framework needs to know that the class is a scheduled task. + +Useful checks are: + +```bash +bin/console scheduled-task:register +bin/console scheduled-task:list +``` + +and, where appropriate: + +```bash +bin/console scheduled-task:run-single +``` + +The current Core `ScheduledTaskGenerator` generates the task class and its `shopware.scheduled.task` registration. It does not generate a separate task handler as part of that scaffold. + +## Configuration fields: static validity is only one checkpoint + +A plugin configuration field lives inside an existing `config.xml`. A successful XML/schema validation proves that the structure is valid, but the user-facing acceptance criterion is stronger: the field should be available in the Administration. + +Treat these as separate checks: + +```text +config.xml validates + ↓ +configuration key exists + ↓ +Administration shows the field +``` + +When adding a field to an existing configuration, preserve unrelated fields and card structure. Avoid replacing the whole document unless you intentionally want to replace the configuration. + +## Administration modules: a build that passes is not the finish line + +Administration features commonly span: + +```text +entry point +module registration +route +page component +template +localized snippets +``` + +An Administration build can succeed while the module is still absent from the UI. Verify both: + +1. `shopware-cli project admin-build` (or the equivalent Administration build workflow for your project) +2. The module appears in the Administration and its initial route opens. + +Keep English and German snippet keys aligned with the module registration. Missing or inconsistent snippets should be treated as wiring problems, not just translation problems. + +## Tests: distinguish generation from test infrastructure + +The focused test-generator story is intentionally narrower than “set up testing”. A generated unit test should follow the plugin's existing PHPUnit path and Composer development autoloading, but the generator should not silently create or repair the plugin's test infrastructure. + +Check: + +```text +phpunit.xml suite definition +Composer autoload-dev namespace +expected tests/ path +actual test class namespace +``` + +Then use the plugin's configured PHPUnit workflow to verify discovery when PHPUnit is available. + +## A practical troubleshooting sequence + +When a newly generated feature does not work, use this order: + +```text +1. Does the file/class exist? +2. Does its namespace match Composer autoloading? +3. Is the service registered? +4. Is the route/command/task/module/test discovered? +5. Did you rebuild or clear the relevant cache? +6. Can you execute the feature for real? +7. If it fails, is the failure in your feature or in the framework's authentication/build/runtime layer? +``` + +This sequence helps avoid changing several unrelated things at once. + +## Why this matters for future `shopware-cli extension create` generators + +The Shopware CLI work tracked in [#1255](https://github.com/shopware/shopware-cli/issues/1255) and [#1280](https://github.com/shopware/shopware-cli/issues/1280) aims to make extension generation more contextual: an existing plugin should be inspected, framework-specific conventions should be derived where possible, and the generator should create a coordinated feature rather than just a single file. + +The generator proposals are not equally sized. A command or subscriber is relatively bounded. A Storefront controller, Store API route, or Administration module crosses more files and verification layers. That is why a good generator should: + +- ask only for information the developer cannot safely derive, +- preserve existing unrelated code and configuration, +- detect collisions before writing, +- respect the Shopware version targeted by the plugin, +- and verify discovery or runtime behavior where static validation is insufficient. + +These principles make both the generated project and the documentation easier for developers and AI coding assistants to reason about. + +## Reference implementations + +When you need to understand what the current Core scaffold actually generates, the source of truth is the scaffolding generator in `shopware/shopware`, for example: + +- [CommandGenerator.php](https://github.com/shopware/shopware/blob/trunk/src/Core/Framework/Plugin/Command/Scaffolding/Generator/CommandGenerator.php) +- [ScheduledTaskGenerator.php](https://github.com/shopware/shopware/blob/trunk/src/Core/Framework/Plugin/Command/Scaffolding/Generator/ScheduledTaskGenerator.php) +- [StorefrontControllerGenerator.php](https://github.com/shopware/shopware/blob/trunk/src/Core/Framework/Plugin/Command/Scaffolding/Generator/StorefrontControllerGenerator.php) +- [StoreApiRouteGenerator.php](https://github.com/shopware/shopware/blob/trunk/src/Core/Framework/Plugin/Command/Scaffolding/Generator/StoreApiRouteGenerator.php) + +For proposed CLI-side generators, use the corresponding Shopware CLI issue as the contract and then verify the implementation against the CLI repository before documenting it as available. From 8c9455464f718747070013e36ee219675d619e9c Mon Sep 17 00:00:00 2001 From: somethings Date: Fri, 21 Aug 2026 15:41:29 +0200 Subject: [PATCH 02/26] Update scaffolding-and-generator-troubleshooting.md --- .../plugins/scaffolding-and-generator-troubleshooting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guides/plugins/plugins/scaffolding-and-generator-troubleshooting.md b/guides/plugins/plugins/scaffolding-and-generator-troubleshooting.md index 95b275bdbc..9b37f2af83 100644 --- a/guides/plugins/plugins/scaffolding-and-generator-troubleshooting.md +++ b/guides/plugins/plugins/scaffolding-and-generator-troubleshooting.md @@ -1,11 +1,11 @@ --- nav: - title: Scaffolding and generator troubleshooting + title: Scaffolding and Generator Troubleshooting position: 25 --- -# Scaffolding and generator troubleshooting +# Scaffolding and Generator Troubleshooting Shopware's plugin scaffolding is designed to give developers a useful starting point, but generated files are often part of a larger feature. A PHP class, JavaScript module, configuration entry, route import, or service definition may each be valid on its own while the feature still is not discoverable or usable. From 4a7ef72b04170686726edae35c7fe6e171299be1 Mon Sep 17 00:00:00 2001 From: somethings Date: Fri, 21 Aug 2026 17:27:02 +0200 Subject: [PATCH 03/26] docs: document storefront JavaScript scaffolding runtime wiring --- .../storefront-js-scaffolding-runtime.md | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 guides/plugins/plugins/storefront-js-scaffolding-runtime.md diff --git a/guides/plugins/plugins/storefront-js-scaffolding-runtime.md b/guides/plugins/plugins/storefront-js-scaffolding-runtime.md new file mode 100644 index 0000000000..bb4fbb05d0 --- /dev/null +++ b/guides/plugins/plugins/storefront-js-scaffolding-runtime.md @@ -0,0 +1,115 @@ +--- +nav: + title: Storefront JavaScript scaffolding runtime wiring + position: 26 + +--- + +# Storefront JavaScript scaffolding runtime wiring + +A Storefront JavaScript example is not just a JavaScript class. The current Core scaffolder coordinates a plugin class, the Storefront `main.js` registration, and a Twig template containing the matching initialization target. A successful Storefront build is not sufficient to prove that the plugin runs. + +The current Core example uses the legacy Storefront `PluginManager` model. The generated plugin class imports the Storefront `Plugin` base class from `src/plugin-system/plugin.class`, and `main.js` registers the class with `window.PluginManager`. The Twig example provides the corresponding selector target. Verify the exact convention against the Shopware version you target before copying this pattern. + +## Runtime chain + +Think of the generated feature as one chain: + +```text +plugin class + ↓ +Storefront main.js import + ↓ +PluginManager registration + selector + ↓ +Twig/template target + ↓ +storefront-build + ↓ +cache clear when templates change + ↓ +rendered DOM contains the selector + ↓ +PluginManager initializes the class +``` + +A failure anywhere in this chain can look like a JavaScript problem even though the JavaScript class itself is valid. + +## Verify in layers + +Build first: + +```bash +shopware-cli project storefront-build +``` + +Then clear the Shopware cache after changing Twig or other runtime-discovered template configuration: + +```bash +shopware-cli project console cache:clear +``` + +Verify that the selector is actually in the server-rendered page: + +```bash +curl -s http://127.0.0.1:8000/ | grep -o 'data-example-plugin' +``` + +Then verify it in the browser: + +```js +document.querySelector('[data-example-plugin]') +``` + +Finally verify runtime initialization in the browser console. A useful minimal test is to log from the plugin's `init()` method. + +## Common failure: `Illegal constructor` + +A JavaScript class can compile and be registered but still fail during initialization if it extends the wrong base class. The current Core scaffold imports the Storefront `Plugin` class explicitly: + +```js +import Plugin from 'src/plugin-system/plugin.class'; + +export default class ExamplePlugin extends Plugin { + init() { + // behavior + } +} +``` + +Do not replace that with an arbitrary browser or global constructor simply because a similarly named object exists on `window`. + +## Common failure: the selector is missing + +If this returns `null`: + +```js +document.querySelector('[data-example-plugin]') +``` + +check the Twig layer before changing JavaScript. The current Core scaffold's Twig stub extends the Storefront content template and places the matching selector inside the `base_main_inner` block. + +If the template file is correct but the selector is still absent from the rendered HTML, clear the Shopware cache and test the actual page that renders the overridden template. A successful asset build does not refresh server-rendered Twig output by itself. + +## Common failure: build succeeds but the plugin never runs + +If the Storefront build succeeds but there is no initialization log, separate the failure into: + +```text +selector absent from DOM + → template/cache problem + +selector present, no registration + → main.js / registration problem + +registration present, initialization error + → plugin class / Storefront API problem +``` + +This avoids changing the JavaScript class when the actual problem is that the template was never rendered. + +## Why the generator needs context + +The current Core generator writes all of the coordinating pieces together. The proposed `shopware-cli extension create storefront-js-plugin` story goes further: it must account for the supported Storefront extension model, the plugin's existing entry points, selectors, template hooks, build paths, and collisions, and it needs a runtime-oriented acceptance check. + +That means this generator should be considered a coordinated feature generator, not a single-file template generator. From 636784e09f35de74cbd5c6c4c4cf514bb9997da9 Mon Sep 17 00:00:00 2001 From: somethings Date: Fri, 21 Aug 2026 17:36:07 +0200 Subject: [PATCH 04/26] docs: distribute scaffolding troubleshooting by extension point --- .../troubleshoot-module-discovery.md | 39 ++++++ .../store-api/troubleshoot-store-api-route.md | 53 ++++++++ .../troubleshoot-scheduled-task.md | 44 +++++++ .../storefront-js-scaffolding-runtime.md | 115 ------------------ ...eshoot-javascript-plugin-initialization.md | 114 +++++++++++++++++ 5 files changed, 250 insertions(+), 115 deletions(-) create mode 100644 guides/plugins/plugins/administration/module-component-management/troubleshoot-module-discovery.md create mode 100644 guides/plugins/plugins/framework/store-api/troubleshoot-store-api-route.md create mode 100644 guides/plugins/plugins/plugin-fundamentals/troubleshoot-scheduled-task.md delete mode 100644 guides/plugins/plugins/storefront-js-scaffolding-runtime.md create mode 100644 guides/plugins/plugins/storefront/javascript/troubleshoot-javascript-plugin-initialization.md diff --git a/guides/plugins/plugins/administration/module-component-management/troubleshoot-module-discovery.md b/guides/plugins/plugins/administration/module-component-management/troubleshoot-module-discovery.md new file mode 100644 index 0000000000..7703af9d28 --- /dev/null +++ b/guides/plugins/plugins/administration/module-component-management/troubleshoot-module-discovery.md @@ -0,0 +1,39 @@ +--- +nav: + title: Troubleshoot module discovery + position: 50 +--- + +# Troubleshoot Administration module discovery + +An Administration module is not a single JavaScript file. A minimal module normally coordinates an Administration entry point, module registration, route, page component or template, and snippets. + +```text +Administration main.js + ↓ +module import and registration + ↓ +route + ↓ +page component / template + ↓ +localized snippets + ↓ +Administration build + ↓ +module appears in the UI + ↓ +initial route opens +``` + +A successful Administration build proves compilation, not UI discovery. After changing Administration source, run the project Administration build and then verify the result in the Administration itself: + +```bash +shopware-cli project admin-build +``` + +If the build succeeds but the module is missing, check that the module is imported from the extension's Administration entry point, that the registered navigation path matches the route, and that snippet keys referenced by the module exist. Refresh the Administration after rebuilding. + +Treat “module appears” and “initial route opens” as separate checks. The first proves registration/discovery; the second catches broken route-to-component wiring. + +When adding generated Administration code to an existing plugin, preserve existing entry-point imports, routes, snippets, and unrelated modules. A generator has to merge with this existing graph rather than replace it. diff --git a/guides/plugins/plugins/framework/store-api/troubleshoot-store-api-route.md b/guides/plugins/plugins/framework/store-api/troubleshoot-store-api-route.md new file mode 100644 index 0000000000..4f422f082f --- /dev/null +++ b/guides/plugins/plugins/framework/store-api/troubleshoot-store-api-route.md @@ -0,0 +1,53 @@ +--- +nav: + title: Troubleshoot a Store API route + position: 50 +--- + +# Troubleshoot a Store API route + +A Store API route has several independent framework boundaries. Check them separately instead of treating every HTTP error as a routing error. + +```text +route class / response classes + ↓ +route import + ↓ +service registration + dependencies + ↓ +router discovery + ↓ +Store API authentication / SalesChannelContext + ↓ +route execution +``` + +## Check discovery first + +```bash +bin/console debug:router | grep your-route +``` + +A route appearing here does not prove that its service can be constructed or that a real Store API request can execute. + +## Then make a real request + +Call the Store API endpoint and inspect the response boundary. Store API requests require Store API authentication context. For example, a response saying that the `sw-access-key` header is required means the request reached Shopware's Store API authentication layer. That is different from a `404` caused by an undiscovered route and different again from a container error constructing the route service. + +```text +404 / route missing + → route import or route attributes + +container / constructor error + → service registration or dependencies + +401 requiring sw-access-key + → routing succeeded; authentication is now the boundary + +route-specific error after authentication + → endpoint implementation +``` + +Do not use an Admin API helper as a substitute for a Store API request. They use different authentication models. + +For generated routes, verify both router discovery and a real Store API request. Static file generation alone does not prove the feature is callable. diff --git a/guides/plugins/plugins/plugin-fundamentals/troubleshoot-scheduled-task.md b/guides/plugins/plugins/plugin-fundamentals/troubleshoot-scheduled-task.md new file mode 100644 index 0000000000..c1e380f21c --- /dev/null +++ b/guides/plugins/plugins/plugin-fundamentals/troubleshoot-scheduled-task.md @@ -0,0 +1,44 @@ +--- +nav: + title: Troubleshoot scheduled task registration + position: 50 +--- + +# Troubleshoot scheduled task registration + +A scheduled-task class only defines the task identity and schedule. Shopware must also discover and register it before it appears in the scheduled-task table. + +For the current Core scaffolding generator, the generated scheduled-task example consists of the task class plus its `shopware.scheduled.task` service registration. The scaffolder does not generate a separate handler as part of that example. Do not copy an older handler pattern without checking the Core API used by your target Shopware version. + +After adding or changing task registration, clear the container cache when needed and register scheduled tasks: + +```bash +bin/console scheduled-task:register +bin/console scheduled-task:list +``` + +For a focused check: + +```bash +bin/console scheduled-task:list | grep your-task-name +``` + +When appropriate, run one task directly: + +```bash +bin/console scheduled-task:run-single your-task-name +``` + +```text +task class exists + ↓ +service has shopware.scheduled.task tag + ↓ +scheduled-task:register discovers it + ↓ +scheduled-task:list shows it + ↓ +runtime execution +``` + +If plugin activation reports that an already-active plugin is being skipped, do not assume that activation rebuilt the Symfony container. Cache/container refresh and scheduled-task registration are separate operations. diff --git a/guides/plugins/plugins/storefront-js-scaffolding-runtime.md b/guides/plugins/plugins/storefront-js-scaffolding-runtime.md deleted file mode 100644 index bb4fbb05d0..0000000000 --- a/guides/plugins/plugins/storefront-js-scaffolding-runtime.md +++ /dev/null @@ -1,115 +0,0 @@ ---- -nav: - title: Storefront JavaScript scaffolding runtime wiring - position: 26 - ---- - -# Storefront JavaScript scaffolding runtime wiring - -A Storefront JavaScript example is not just a JavaScript class. The current Core scaffolder coordinates a plugin class, the Storefront `main.js` registration, and a Twig template containing the matching initialization target. A successful Storefront build is not sufficient to prove that the plugin runs. - -The current Core example uses the legacy Storefront `PluginManager` model. The generated plugin class imports the Storefront `Plugin` base class from `src/plugin-system/plugin.class`, and `main.js` registers the class with `window.PluginManager`. The Twig example provides the corresponding selector target. Verify the exact convention against the Shopware version you target before copying this pattern. - -## Runtime chain - -Think of the generated feature as one chain: - -```text -plugin class - ↓ -Storefront main.js import - ↓ -PluginManager registration + selector - ↓ -Twig/template target - ↓ -storefront-build - ↓ -cache clear when templates change - ↓ -rendered DOM contains the selector - ↓ -PluginManager initializes the class -``` - -A failure anywhere in this chain can look like a JavaScript problem even though the JavaScript class itself is valid. - -## Verify in layers - -Build first: - -```bash -shopware-cli project storefront-build -``` - -Then clear the Shopware cache after changing Twig or other runtime-discovered template configuration: - -```bash -shopware-cli project console cache:clear -``` - -Verify that the selector is actually in the server-rendered page: - -```bash -curl -s http://127.0.0.1:8000/ | grep -o 'data-example-plugin' -``` - -Then verify it in the browser: - -```js -document.querySelector('[data-example-plugin]') -``` - -Finally verify runtime initialization in the browser console. A useful minimal test is to log from the plugin's `init()` method. - -## Common failure: `Illegal constructor` - -A JavaScript class can compile and be registered but still fail during initialization if it extends the wrong base class. The current Core scaffold imports the Storefront `Plugin` class explicitly: - -```js -import Plugin from 'src/plugin-system/plugin.class'; - -export default class ExamplePlugin extends Plugin { - init() { - // behavior - } -} -``` - -Do not replace that with an arbitrary browser or global constructor simply because a similarly named object exists on `window`. - -## Common failure: the selector is missing - -If this returns `null`: - -```js -document.querySelector('[data-example-plugin]') -``` - -check the Twig layer before changing JavaScript. The current Core scaffold's Twig stub extends the Storefront content template and places the matching selector inside the `base_main_inner` block. - -If the template file is correct but the selector is still absent from the rendered HTML, clear the Shopware cache and test the actual page that renders the overridden template. A successful asset build does not refresh server-rendered Twig output by itself. - -## Common failure: build succeeds but the plugin never runs - -If the Storefront build succeeds but there is no initialization log, separate the failure into: - -```text -selector absent from DOM - → template/cache problem - -selector present, no registration - → main.js / registration problem - -registration present, initialization error - → plugin class / Storefront API problem -``` - -This avoids changing the JavaScript class when the actual problem is that the template was never rendered. - -## Why the generator needs context - -The current Core generator writes all of the coordinating pieces together. The proposed `shopware-cli extension create storefront-js-plugin` story goes further: it must account for the supported Storefront extension model, the plugin's existing entry points, selectors, template hooks, build paths, and collisions, and it needs a runtime-oriented acceptance check. - -That means this generator should be considered a coordinated feature generator, not a single-file template generator. diff --git a/guides/plugins/plugins/storefront/javascript/troubleshoot-javascript-plugin-initialization.md b/guides/plugins/plugins/storefront/javascript/troubleshoot-javascript-plugin-initialization.md new file mode 100644 index 0000000000..84909900cb --- /dev/null +++ b/guides/plugins/plugins/storefront/javascript/troubleshoot-javascript-plugin-initialization.md @@ -0,0 +1,114 @@ +--- +nav: + title: Troubleshoot JavaScript plugin initialization + position: 50 +--- + +# Troubleshoot JavaScript plugin initialization + +A Storefront JavaScript feature is a chain of coordinated pieces. A successful asset build proves that the JavaScript compiled; it does not prove that the browser can find an initialization target or instantiate the plugin. + +For the current Core scaffolding example, the chain is: + +```text +plugin class + ↓ +Storefront main.js import + ↓ +PluginManager registration + selector + ↓ +Twig/template target + ↓ +Storefront build + ↓ +Shopware cache / rendered template + ↓ +PluginManager initializes the class +``` + +## Use the Storefront Plugin base class + +The current Core scaffold imports the Storefront `Plugin` base class explicitly: + +```js +import Plugin from 'src/plugin-system/plugin.class'; + +export default class ExamplePlugin extends Plugin { + init() { + // behavior + } +} +``` + +Do not substitute an arbitrary global constructor such as `window.Plugin`. Code can compile successfully and still fail at runtime with an error such as `TypeError: Illegal constructor` when the wrong base class is used. + +## Verify the registration and selector together + +The current Core example registers the plugin in the Storefront entry point: + +```js +import ExamplePlugin from './example-plugin/example-plugin.plugin'; + +const PluginManager = window.PluginManager; +PluginManager.register('ExamplePlugin', ExamplePlugin, '[data-example-plugin]'); +``` + +The selector in the registration must match an element rendered by Twig. The current scaffold creates a template hook such as: + +```twig + +``` + +If the selector and template disagree, the plugin can build without ever initializing. + +## Build, clear cache, then verify runtime + +Build the Storefront assets: + +```bash +shopware-cli project storefront-build +``` + +When you also changed Twig, clear Shopware's cache before deciding that the template override failed: + +```bash +shopware-cli project console cache:clear +``` + +A Storefront build and a Shopware/Twig cache clear prove different things. Building assets does not by itself guarantee that a changed Twig template is present in the next server-rendered response. + +Check the server-rendered HTML first: + +```bash +curl -s http://127.0.0.1:8000/ | grep -o 'data-example-plugin' +``` + +Then check the DOM in the browser console: + +```js +document.querySelector('[data-example-plugin]') +``` + +Finally verify that `init()` runs. During development, a temporary `console.log()` in `init()` is a simple runtime check. + +## Localize the failure before changing code + +```text +selector absent from server-rendered HTML + → template inheritance / cache / tested page + +selector absent from DOM + → rendered markup / client-side DOM changes + +selector present, plugin not registered + → main.js import / PluginManager registration + +plugin registered, initialization error + → plugin class / Storefront API / version-specific convention +``` + +This distinction is especially important for generated code: file existence and compilation are weaker checks than runtime initialization. + +## Version-specific Storefront models + +The current Core scaffolder uses the `PluginManager` model shown above. Storefront extension models can evolve. When generating or copying scaffolding, verify the convention against the Shopware version you target instead of assuming that a legacy registration pattern is version-neutral. From 059230879b994bae8658fbca1a5bd5bc07af07e8 Mon Sep 17 00:00:00 2001 From: somethings Date: Fri, 21 Aug 2026 17:47:49 +0200 Subject: [PATCH 05/26] docs: reframe scaffolding findings as feature lifecycles --- .../administration-module-lifecycle.md | 37 +++ .../troubleshoot-module-discovery.md | 39 --- .../store-api/store-api-route-lifecycle.md | 40 +++ .../store-api/troubleshoot-store-api-route.md | 53 ---- .../scheduled-task-lifecycle.md | 36 +++ .../troubleshoot-scheduled-task.md | 44 ---- ...affolding-and-generator-troubleshooting.md | 241 ------------------ .../plugins/storefront/javascript/index.md | 3 +- .../storefront-javascript-plugin-lifecycle.md | 77 ++++++ ...eshoot-javascript-plugin-initialization.md | 114 --------- .../understanding-plugin-feature-wiring.md | 84 ++++++ 11 files changed, 276 insertions(+), 492 deletions(-) create mode 100644 guides/plugins/plugins/administration/module-component-management/administration-module-lifecycle.md delete mode 100644 guides/plugins/plugins/administration/module-component-management/troubleshoot-module-discovery.md create mode 100644 guides/plugins/plugins/framework/store-api/store-api-route-lifecycle.md delete mode 100644 guides/plugins/plugins/framework/store-api/troubleshoot-store-api-route.md create mode 100644 guides/plugins/plugins/plugin-fundamentals/scheduled-task-lifecycle.md delete mode 100644 guides/plugins/plugins/plugin-fundamentals/troubleshoot-scheduled-task.md delete mode 100644 guides/plugins/plugins/scaffolding-and-generator-troubleshooting.md create mode 100644 guides/plugins/plugins/storefront/javascript/storefront-javascript-plugin-lifecycle.md delete mode 100644 guides/plugins/plugins/storefront/javascript/troubleshoot-javascript-plugin-initialization.md create mode 100644 guides/plugins/plugins/understanding-plugin-feature-wiring.md diff --git a/guides/plugins/plugins/administration/module-component-management/administration-module-lifecycle.md b/guides/plugins/plugins/administration/module-component-management/administration-module-lifecycle.md new file mode 100644 index 0000000000..b3389d0c0b --- /dev/null +++ b/guides/plugins/plugins/administration/module-component-management/administration-module-lifecycle.md @@ -0,0 +1,37 @@ +--- +nav: + title: Administration module lifecycle + position: 15 +--- + +# Administration module lifecycle + +An Administration module connects an entry point, module registration, routes, page components or templates, snippets, the Administration build, and the UI. + +```text +main.js → module import/registration → route → component/template → snippets → build → UI → initial route +``` + +For implementation, see [Add Custom Module](./add-custom-module.md). + +## What each stage proves + +The plugin Administration `main.js` must import the module. The module then registers through `Shopware.Module.register()`. A valid module file remains invisible if the entry point never imports it. + +Routes used by navigation must resolve to registered page components, and snippet keys used by labels and titles must exist. + +Build the Administration with: + +```bash +shopware-cli project admin-build +``` + +A successful build proves compilation. After rebuilding, refresh the Administration and separately verify that the module appears and that its initial route opens. + +## Troubleshooting by boundary + +If the build succeeds but the module is missing, inspect the `main.js` import, module registration, navigation path, and snippets before changing the page component. + +If the module appears but its route fails, inspect route-to-component wiring. + +When adding generated code to an existing plugin, preserve existing imports, routes, snippets, and unrelated modules rather than replacing the module graph. diff --git a/guides/plugins/plugins/administration/module-component-management/troubleshoot-module-discovery.md b/guides/plugins/plugins/administration/module-component-management/troubleshoot-module-discovery.md deleted file mode 100644 index 7703af9d28..0000000000 --- a/guides/plugins/plugins/administration/module-component-management/troubleshoot-module-discovery.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -nav: - title: Troubleshoot module discovery - position: 50 ---- - -# Troubleshoot Administration module discovery - -An Administration module is not a single JavaScript file. A minimal module normally coordinates an Administration entry point, module registration, route, page component or template, and snippets. - -```text -Administration main.js - ↓ -module import and registration - ↓ -route - ↓ -page component / template - ↓ -localized snippets - ↓ -Administration build - ↓ -module appears in the UI - ↓ -initial route opens -``` - -A successful Administration build proves compilation, not UI discovery. After changing Administration source, run the project Administration build and then verify the result in the Administration itself: - -```bash -shopware-cli project admin-build -``` - -If the build succeeds but the module is missing, check that the module is imported from the extension's Administration entry point, that the registered navigation path matches the route, and that snippet keys referenced by the module exist. Refresh the Administration after rebuilding. - -Treat “module appears” and “initial route opens” as separate checks. The first proves registration/discovery; the second catches broken route-to-component wiring. - -When adding generated Administration code to an existing plugin, preserve existing entry-point imports, routes, snippets, and unrelated modules. A generator has to merge with this existing graph rather than replace it. diff --git a/guides/plugins/plugins/framework/store-api/store-api-route-lifecycle.md b/guides/plugins/plugins/framework/store-api/store-api-route-lifecycle.md new file mode 100644 index 0000000000..cd3821afa0 --- /dev/null +++ b/guides/plugins/plugins/framework/store-api/store-api-route-lifecycle.md @@ -0,0 +1,40 @@ +--- +nav: + title: Store API route lifecycle + position: 15 +--- + +# Store API route lifecycle + +A Store API route crosses several independent boundaries: + +```text +route/response classes → routes.php import → service registration → router → authentication/context → execution +``` + +For implementation, see [Add Store API Route](./add-store-api-route.md). + +## Discovery + +Use the Symfony router to prove Shopware discovered the route: + +```bash +bin/console debug:router | grep your-route +``` + +A route appearing here does not prove that its service can be constructed or that a Store API request can execute. + +## Authentication and execution + +Make a real Store API request to test the next boundary. A response requiring `sw-access-key` means the request reached Store API authentication; it is not evidence that the route is missing. + +```text +404 / route absent → route attributes or routes.php import +container error → service registration or dependencies +401 requiring sw-access-key → routing succeeded; authentication is now the boundary +endpoint-specific response/error → route implementation +``` + +Admin API and Store API helpers use different authentication models, so do not substitute one for the other when verifying a Store API route. + +Generated PHP files are therefore only the first check. Verify router discovery and a real authenticated request as separate acceptance steps. diff --git a/guides/plugins/plugins/framework/store-api/troubleshoot-store-api-route.md b/guides/plugins/plugins/framework/store-api/troubleshoot-store-api-route.md deleted file mode 100644 index 4f422f082f..0000000000 --- a/guides/plugins/plugins/framework/store-api/troubleshoot-store-api-route.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -nav: - title: Troubleshoot a Store API route - position: 50 ---- - -# Troubleshoot a Store API route - -A Store API route has several independent framework boundaries. Check them separately instead of treating every HTTP error as a routing error. - -```text -route class / response classes - ↓ -route import - ↓ -service registration + dependencies - ↓ -router discovery - ↓ -Store API authentication / SalesChannelContext - ↓ -route execution -``` - -## Check discovery first - -```bash -bin/console debug:router | grep your-route -``` - -A route appearing here does not prove that its service can be constructed or that a real Store API request can execute. - -## Then make a real request - -Call the Store API endpoint and inspect the response boundary. Store API requests require Store API authentication context. For example, a response saying that the `sw-access-key` header is required means the request reached Shopware's Store API authentication layer. That is different from a `404` caused by an undiscovered route and different again from a container error constructing the route service. - -```text -404 / route missing - → route import or route attributes - -container / constructor error - → service registration or dependencies - -401 requiring sw-access-key - → routing succeeded; authentication is now the boundary - -route-specific error after authentication - → endpoint implementation -``` - -Do not use an Admin API helper as a substitute for a Store API request. They use different authentication models. - -For generated routes, verify both router discovery and a real Store API request. Static file generation alone does not prove the feature is callable. diff --git a/guides/plugins/plugins/plugin-fundamentals/scheduled-task-lifecycle.md b/guides/plugins/plugins/plugin-fundamentals/scheduled-task-lifecycle.md new file mode 100644 index 0000000000..027870a0e0 --- /dev/null +++ b/guides/plugins/plugins/plugin-fundamentals/scheduled-task-lifecycle.md @@ -0,0 +1,36 @@ +--- +nav: + title: Scheduled task lifecycle + position: 105 +--- + +# Scheduled task lifecycle + +A scheduled task must be discovered, registered, persisted, scheduled, and eventually executed through Shopware's task runner and message queue. + +For the broader implementation, including handlers and execution, see [Add Scheduled Task](./add-scheduled-task.md). + +The current Core scaffolding example for a scheduled task creates the task class and its `shopware.scheduled.task` registration. Treat that focused scaffold separately from the complete application pattern described in the implementation guide, and verify conventions against the Shopware version you target. + +## Registration lifecycle + +```text +task class → services.php/tag → scheduled-task:register → scheduled-task:list → persisted schedule → runner/message queue → execution +``` + +Useful discovery checks are: + +```bash +bin/console scheduled-task:register +bin/console scheduled-task:list | grep your-task-name +``` + +## Troubleshooting by boundary + +```text +class-not-found → code / namespace / autoloading +task absent from scheduled-task:list → service registration / discovery +task registered but not executing → schedule state / runner / message queue / handler +``` + +Changing service registration can require a cache/container refresh. Re-running plugin activation for an already-active plugin does not by itself prove that the Symfony container was rebuilt. diff --git a/guides/plugins/plugins/plugin-fundamentals/troubleshoot-scheduled-task.md b/guides/plugins/plugins/plugin-fundamentals/troubleshoot-scheduled-task.md deleted file mode 100644 index c1e380f21c..0000000000 --- a/guides/plugins/plugins/plugin-fundamentals/troubleshoot-scheduled-task.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -nav: - title: Troubleshoot scheduled task registration - position: 50 ---- - -# Troubleshoot scheduled task registration - -A scheduled-task class only defines the task identity and schedule. Shopware must also discover and register it before it appears in the scheduled-task table. - -For the current Core scaffolding generator, the generated scheduled-task example consists of the task class plus its `shopware.scheduled.task` service registration. The scaffolder does not generate a separate handler as part of that example. Do not copy an older handler pattern without checking the Core API used by your target Shopware version. - -After adding or changing task registration, clear the container cache when needed and register scheduled tasks: - -```bash -bin/console scheduled-task:register -bin/console scheduled-task:list -``` - -For a focused check: - -```bash -bin/console scheduled-task:list | grep your-task-name -``` - -When appropriate, run one task directly: - -```bash -bin/console scheduled-task:run-single your-task-name -``` - -```text -task class exists - ↓ -service has shopware.scheduled.task tag - ↓ -scheduled-task:register discovers it - ↓ -scheduled-task:list shows it - ↓ -runtime execution -``` - -If plugin activation reports that an already-active plugin is being skipped, do not assume that activation rebuilt the Symfony container. Cache/container refresh and scheduled-task registration are separate operations. diff --git a/guides/plugins/plugins/scaffolding-and-generator-troubleshooting.md b/guides/plugins/plugins/scaffolding-and-generator-troubleshooting.md deleted file mode 100644 index 9b37f2af83..0000000000 --- a/guides/plugins/plugins/scaffolding-and-generator-troubleshooting.md +++ /dev/null @@ -1,241 +0,0 @@ ---- -nav: - title: Scaffolding and Generator Troubleshooting - position: 25 - ---- - -# Scaffolding and Generator Troubleshooting - -Shopware's plugin scaffolding is designed to give developers a useful starting point, but generated files are often part of a larger feature. A PHP class, JavaScript module, configuration entry, route import, or service definition may each be valid on its own while the feature still is not discoverable or usable. - -This guide explains the mental model behind scaffolding and gives a troubleshooting path that works well both for humans and for coding assistants. - -::: info -This guide describes the current Core `bin/console plugin:create` scaffolding workflow and the conventions it generates. The newer `shopware-cli extension create ...` generator work is being developed separately in the Shopware CLI repository. See [#1255](https://github.com/shopware/shopware-cli/issues/1255) and [#1280](https://github.com/shopware/shopware-cli/issues/1280). -::: - -## Start with the smallest scaffold - -If you are not sure which example components you need, prefer a minimal plugin scaffold and add features when you need them. The current Core `plugin:create` command supports both a minimal skeleton and optional example components. - -```bash -bin/console plugin:create SwagBasicExample --no-scaffold -``` - -The interactive form asks whether optional files should be scaffolded. The optional generators are examples: they are useful for learning conventions, but you should review the generated files rather than assuming that selecting a generator creates your final production implementation. - -For the current Core scaffold, examples include a command, scheduled task, event subscriber, Storefront controller, Store API route, Administration module, Storefront JavaScript plugin, custom field set, and entities. The exact generated files depend on the component and your Shopware version. See the [Creating Plugins](./creating-plugins.md) guide for the current command options. - -## The important mental model: a feature is usually a set of coordinated pieces - -When a generated feature does not work, ask which of these layers are involved: - -1. **Code** — Is the class/component present, with the right namespace and path? -2. **Registration** — Does the service container know about it? -3. **Discovery** — Is the route, command, module, task, or test imported/registered? -4. **Build/cache** — Has the relevant cache or Administration build been refreshed? -5. **Runtime verification** — Can you actually call/open/run the feature? - -A common mistake is stopping after layer 1 or layer 3. For example, a storefront controller can appear in `debug:router` and still fail at request time if its service registration is incomplete. - -## Why namespace and autoloading matter - -Plugin Composer autoloading commonly maps the plugin namespace prefix to `src/`: - -```json -"autoload": { - "psr-4": { - "Swag\\BasicExample\\": "src/" - } -} -``` - -A file at: - -```text -src/Subscriber/OrderPlacedSubscriber.php -``` - -therefore needs a matching namespace such as: - -```php -namespace Swag\BasicExample\Subscriber; -``` - -When a class is not found, compare these three values first: - -```text -composer.json PSR-4 prefix - ↓ -PHP namespace - ↓ -filesystem path -``` - -They must describe the same class. - -## Service registration: what the tags mean - -Many Shopware features are Symfony services. The service container needs to know how a class should participate in the framework. - -For example, an event subscriber can use: - -```xml - - - -``` - -The PHP class declares which events it subscribes to. The `kernel.event_subscriber` tag tells Symfony to treat the service as an event subscriber and register it with the event dispatcher. - -Likewise, a console command is registered with the `console.command` tag, while a scheduled task uses the `shopware.scheduled.task` tag. - -The current Core scaffolding generators append service definitions to `src/Resources/config/services.php` for these features rather than asking you to remember every registration detail. - -::: warning -Current Core code also supports legacy XML service configuration in existing plugins, but `services.xml` is deprecated. When you touch an older plugin, check whether the current version expects `services.php` or `services.yaml` before copying an example blindly. -::: - -## Routes: discovery is not the same as reachability - -A route-based feature normally has at least two stages of verification: - -```bash -bin/console debug:router | grep my-route -``` - -proves that the route is in the router. It does **not** prove that the request will execute successfully. - -For a Storefront controller, verify the actual URL as well. If the route is visible but the request returns a server error, inspect the controller's service definition and route configuration. - -For Store API routes, a request may reach Shopware but return `401 Unauthorized` because Store API authentication is required. A 401 from the authentication layer is different from a 404 caused by an undiscovered route. - -## Commands: use `bin/console` as the verification target - -For a generated console command, use both: - -```bash -bin/console list | grep my-prefix -``` - -and then run the command itself: - -```bash -bin/console my-prefix:my-command -``` - -The first verifies discovery. The second verifies that the command can actually execute. - -The same principle applies to other generators: prefer a verification command or a real request over a file listing. - -## Scheduled tasks: verify the task lifecycle - -A scheduled task is more than a PHP class. The framework needs to know that the class is a scheduled task. - -Useful checks are: - -```bash -bin/console scheduled-task:register -bin/console scheduled-task:list -``` - -and, where appropriate: - -```bash -bin/console scheduled-task:run-single -``` - -The current Core `ScheduledTaskGenerator` generates the task class and its `shopware.scheduled.task` registration. It does not generate a separate task handler as part of that scaffold. - -## Configuration fields: static validity is only one checkpoint - -A plugin configuration field lives inside an existing `config.xml`. A successful XML/schema validation proves that the structure is valid, but the user-facing acceptance criterion is stronger: the field should be available in the Administration. - -Treat these as separate checks: - -```text -config.xml validates - ↓ -configuration key exists - ↓ -Administration shows the field -``` - -When adding a field to an existing configuration, preserve unrelated fields and card structure. Avoid replacing the whole document unless you intentionally want to replace the configuration. - -## Administration modules: a build that passes is not the finish line - -Administration features commonly span: - -```text -entry point -module registration -route -page component -template -localized snippets -``` - -An Administration build can succeed while the module is still absent from the UI. Verify both: - -1. `shopware-cli project admin-build` (or the equivalent Administration build workflow for your project) -2. The module appears in the Administration and its initial route opens. - -Keep English and German snippet keys aligned with the module registration. Missing or inconsistent snippets should be treated as wiring problems, not just translation problems. - -## Tests: distinguish generation from test infrastructure - -The focused test-generator story is intentionally narrower than “set up testing”. A generated unit test should follow the plugin's existing PHPUnit path and Composer development autoloading, but the generator should not silently create or repair the plugin's test infrastructure. - -Check: - -```text -phpunit.xml suite definition -Composer autoload-dev namespace -expected tests/ path -actual test class namespace -``` - -Then use the plugin's configured PHPUnit workflow to verify discovery when PHPUnit is available. - -## A practical troubleshooting sequence - -When a newly generated feature does not work, use this order: - -```text -1. Does the file/class exist? -2. Does its namespace match Composer autoloading? -3. Is the service registered? -4. Is the route/command/task/module/test discovered? -5. Did you rebuild or clear the relevant cache? -6. Can you execute the feature for real? -7. If it fails, is the failure in your feature or in the framework's authentication/build/runtime layer? -``` - -This sequence helps avoid changing several unrelated things at once. - -## Why this matters for future `shopware-cli extension create` generators - -The Shopware CLI work tracked in [#1255](https://github.com/shopware/shopware-cli/issues/1255) and [#1280](https://github.com/shopware/shopware-cli/issues/1280) aims to make extension generation more contextual: an existing plugin should be inspected, framework-specific conventions should be derived where possible, and the generator should create a coordinated feature rather than just a single file. - -The generator proposals are not equally sized. A command or subscriber is relatively bounded. A Storefront controller, Store API route, or Administration module crosses more files and verification layers. That is why a good generator should: - -- ask only for information the developer cannot safely derive, -- preserve existing unrelated code and configuration, -- detect collisions before writing, -- respect the Shopware version targeted by the plugin, -- and verify discovery or runtime behavior where static validation is insufficient. - -These principles make both the generated project and the documentation easier for developers and AI coding assistants to reason about. - -## Reference implementations - -When you need to understand what the current Core scaffold actually generates, the source of truth is the scaffolding generator in `shopware/shopware`, for example: - -- [CommandGenerator.php](https://github.com/shopware/shopware/blob/trunk/src/Core/Framework/Plugin/Command/Scaffolding/Generator/CommandGenerator.php) -- [ScheduledTaskGenerator.php](https://github.com/shopware/shopware/blob/trunk/src/Core/Framework/Plugin/Command/Scaffolding/Generator/ScheduledTaskGenerator.php) -- [StorefrontControllerGenerator.php](https://github.com/shopware/shopware/blob/trunk/src/Core/Framework/Plugin/Command/Scaffolding/Generator/StorefrontControllerGenerator.php) -- [StoreApiRouteGenerator.php](https://github.com/shopware/shopware/blob/trunk/src/Core/Framework/Plugin/Command/Scaffolding/Generator/StoreApiRouteGenerator.php) - -For proposed CLI-side generators, use the corresponding Shopware CLI issue as the contract and then verify the implementation against the CLI repository before documenting it as available. diff --git a/guides/plugins/plugins/storefront/javascript/index.md b/guides/plugins/plugins/storefront/javascript/index.md index 314ecb2ed7..ec37590b08 100644 --- a/guides/plugins/plugins/storefront/javascript/index.md +++ b/guides/plugins/plugins/storefront/javascript/index.md @@ -6,9 +6,10 @@ nav: # Storefront JavaScript -This section explains how to extend and customize the Storefront using JavaScript plugins. It covers creating custom plugins, overriding existing functionality, reacting to events, loading external scripts, and interacting with the Store API. +This section explains how to extend and customize the Storefront using JavaScript plugins. It covers creating custom plugins, understanding how registration and DOM initialization fit together, overriding existing functionality, reacting to events, loading external scripts, and interacting with the Store API. * [Add Custom JavaScript](./add-custom-javascript.md) +* [Storefront JavaScript plugin lifecycle](./storefront-javascript-plugin-lifecycle.md) * [Add JavaScript as Script Tag](./add-javascript-as-script-tag.md) * [Fetching Data with JavaScript](./fetching-data-with-javascript.md) * [Override Existing JavaScript](./override-existing-javascript.md) diff --git a/guides/plugins/plugins/storefront/javascript/storefront-javascript-plugin-lifecycle.md b/guides/plugins/plugins/storefront/javascript/storefront-javascript-plugin-lifecycle.md new file mode 100644 index 0000000000..219e2db035 --- /dev/null +++ b/guides/plugins/plugins/storefront/javascript/storefront-javascript-plugin-lifecycle.md @@ -0,0 +1,77 @@ +--- +nav: + title: Storefront JavaScript plugin lifecycle + position: 45 +--- + +# Storefront JavaScript plugin lifecycle + +A Storefront JavaScript plugin connects a plugin class, the Storefront entry point, `PluginManager`, a DOM selector, a Twig hook, the asset build, and runtime initialization. + +```text +plugin class → main.js → PluginManager + selector → Twig target → build/cache → DOM → init() +``` + +For the step-by-step implementation, see [Add Custom JavaScript](./add-custom-javascript.md). + +## Base class and entry point + +The current Core scaffolding example imports the Storefront base class explicitly: + +```js +import Plugin from 'src/plugin-system/plugin.class'; + +export default class ExamplePlugin extends Plugin { + init() {} +} +``` + +A plausible alternative can compile and still fail at runtime. Use the convention supported by the Shopware version you target. + +Register the plugin from `main.js` with a selector when it should initialize on specific elements: + +```js +import ExamplePlugin from './example-plugin/example-plugin.plugin'; + +window.PluginManager.register('ExamplePlugin', ExamplePlugin, '[data-example-plugin]'); +``` + +## Template target + +The selector is part of the feature contract. The page must render a matching target, for example: + +```twig + +``` + +## Build, cache, and runtime + +Build assets with: + +```bash +shopware-cli project storefront-build +``` + +When Twig changed, also clear Shopware's cache: + +```bash +shopware-cli project console cache:clear +``` + +Then verify the rendered HTML or DOM before debugging the class: + +```js +document.querySelector('[data-example-plugin]') +``` + +A successful asset build proves compilation, not initialization. + +## Troubleshooting by boundary + +```text +selector absent from HTML → template inheritance / cache / tested page +selector present, no registration → main.js / PluginManager +registration present, init error → plugin class / Storefront API / version mismatch +``` + +A temporary `console.log()` in `init()` is a simple final runtime check during development. diff --git a/guides/plugins/plugins/storefront/javascript/troubleshoot-javascript-plugin-initialization.md b/guides/plugins/plugins/storefront/javascript/troubleshoot-javascript-plugin-initialization.md deleted file mode 100644 index 84909900cb..0000000000 --- a/guides/plugins/plugins/storefront/javascript/troubleshoot-javascript-plugin-initialization.md +++ /dev/null @@ -1,114 +0,0 @@ ---- -nav: - title: Troubleshoot JavaScript plugin initialization - position: 50 ---- - -# Troubleshoot JavaScript plugin initialization - -A Storefront JavaScript feature is a chain of coordinated pieces. A successful asset build proves that the JavaScript compiled; it does not prove that the browser can find an initialization target or instantiate the plugin. - -For the current Core scaffolding example, the chain is: - -```text -plugin class - ↓ -Storefront main.js import - ↓ -PluginManager registration + selector - ↓ -Twig/template target - ↓ -Storefront build - ↓ -Shopware cache / rendered template - ↓ -PluginManager initializes the class -``` - -## Use the Storefront Plugin base class - -The current Core scaffold imports the Storefront `Plugin` base class explicitly: - -```js -import Plugin from 'src/plugin-system/plugin.class'; - -export default class ExamplePlugin extends Plugin { - init() { - // behavior - } -} -``` - -Do not substitute an arbitrary global constructor such as `window.Plugin`. Code can compile successfully and still fail at runtime with an error such as `TypeError: Illegal constructor` when the wrong base class is used. - -## Verify the registration and selector together - -The current Core example registers the plugin in the Storefront entry point: - -```js -import ExamplePlugin from './example-plugin/example-plugin.plugin'; - -const PluginManager = window.PluginManager; -PluginManager.register('ExamplePlugin', ExamplePlugin, '[data-example-plugin]'); -``` - -The selector in the registration must match an element rendered by Twig. The current scaffold creates a template hook such as: - -```twig - -``` - -If the selector and template disagree, the plugin can build without ever initializing. - -## Build, clear cache, then verify runtime - -Build the Storefront assets: - -```bash -shopware-cli project storefront-build -``` - -When you also changed Twig, clear Shopware's cache before deciding that the template override failed: - -```bash -shopware-cli project console cache:clear -``` - -A Storefront build and a Shopware/Twig cache clear prove different things. Building assets does not by itself guarantee that a changed Twig template is present in the next server-rendered response. - -Check the server-rendered HTML first: - -```bash -curl -s http://127.0.0.1:8000/ | grep -o 'data-example-plugin' -``` - -Then check the DOM in the browser console: - -```js -document.querySelector('[data-example-plugin]') -``` - -Finally verify that `init()` runs. During development, a temporary `console.log()` in `init()` is a simple runtime check. - -## Localize the failure before changing code - -```text -selector absent from server-rendered HTML - → template inheritance / cache / tested page - -selector absent from DOM - → rendered markup / client-side DOM changes - -selector present, plugin not registered - → main.js import / PluginManager registration - -plugin registered, initialization error - → plugin class / Storefront API / version-specific convention -``` - -This distinction is especially important for generated code: file existence and compilation are weaker checks than runtime initialization. - -## Version-specific Storefront models - -The current Core scaffolder uses the `PluginManager` model shown above. Storefront extension models can evolve. When generating or copying scaffolding, verify the convention against the Shopware version you target instead of assuming that a legacy registration pattern is version-neutral. diff --git a/guides/plugins/plugins/understanding-plugin-feature-wiring.md b/guides/plugins/plugins/understanding-plugin-feature-wiring.md new file mode 100644 index 0000000000..064a8f97d2 --- /dev/null +++ b/guides/plugins/plugins/understanding-plugin-feature-wiring.md @@ -0,0 +1,84 @@ +--- +nav: + title: Understanding plugin feature wiring + position: 25 +--- + +# Understanding plugin feature wiring + +A Shopware plugin feature is usually a chain of coordinated pieces rather than one file. Understanding that chain makes generated code easier to adapt and failures much faster to localize. + +```text +feature intent + ↓ +source artifacts + ↓ +framework registration + ↓ +discovery + ↓ +build / cache + ↓ +runtime + ↓ +verification +``` + +A feature can be correct at one boundary and broken at the next. A PHP class existing on disk does not prove that Symfony registered it. A route appearing in `debug:router` does not prove that its service can be constructed. A successful asset build does not prove that a browser can initialize the feature. + +## Scaffolding is a starting point + +The Core `bin/console plugin:create` command can create a minimal plugin or optional example components. Those examples encode conventions for the Shopware version running the command, but they are still starting points for your implementation. + +If you already know which feature you need, a minimal plugin plus the focused feature guide is often easier to reason about than generating every optional example. + +Generated features also differ in size. A command or subscriber may need a class and service registration. An Administration module or Storefront JavaScript plugin coordinates several source, build, and runtime layers. + +## The five boundaries to check + +1. **Code** — Is the class or component present, and does its namespace/path match autoloading? +2. **Registration** — Is the service, route, module, task, or entry point registered? +3. **Discovery** — Can Shopware prove that it found the feature? +4. **Build/cache** — Did the relevant container, Twig cache, Administration build, or Storefront build refresh? +5. **Runtime** — Can the feature actually execute, open, or initialize? + +Checking these boundaries in order prevents changes to working code when the failure is really registration, authentication, cache, or runtime wiring. + +## Common framework wiring + +### Composer and PSR-4 + +When a class is not found, compare the Composer PSR-4 prefix, PHP namespace, and filesystem path. All three must describe the same class. + +### Symfony services + +Many plugin features are services. Tags such as `kernel.event_subscriber`, `console.command`, and `shopware.scheduled.task` tell Symfony and Shopware how the service participates in the framework. + +### Routes + +Use `debug:router` to prove route discovery, then make a real request to prove reachability. A missing route, container-construction error, authentication response, and endpoint exception are different failure boundaries. + +### Configuration + +A valid `config.xml` proves structural validity. The stronger user-facing check is that the field appears and behaves correctly in the Administration. + +### Builds and caches + +Build commands compile assets. Cache clears refresh runtime-discovered configuration and templates. They are related but not interchangeable; for example, a successful Storefront build does not itself guarantee that changed Twig output is rendered. + +## Focused lifecycle guides + +The following pages explain how the pieces of larger extension points connect from source to runtime: + +- [Storefront JavaScript plugin lifecycle](./storefront/javascript/storefront-javascript-plugin-lifecycle.md) +- [Administration module lifecycle](./administration/module-component-management/administration-module-lifecycle.md) +- [Store API route lifecycle](./framework/store-api/store-api-route-lifecycle.md) +- [Scheduled task lifecycle](./plugin-fundamentals/scheduled-task-lifecycle.md) + +Each lifecycle page links to the corresponding implementation guide and includes troubleshooting at the boundaries where failures commonly occur. + +## Generated code and Shopware versions + +Generated files are tied to the Shopware version whose generator produced them. When a framework convention matters, compare generated output with the corresponding Core scaffolding generator and stubs for the version you target. + +The newer `shopware-cli extension create ...` generator work is developed separately from Core `plugin:create`. Verify CLI generator availability and behavior against `shopware/shopware-cli` rather than assuming the two generator systems are equivalent. From e1878f6b785e22abe0b4f8c22a782af6a0741a5c Mon Sep 17 00:00:00 2001 From: somethings Date: Fri, 21 Aug 2026 17:54:30 +0200 Subject: [PATCH 06/26] docs: align lifecycle pages with navigation conventions --- guides/plugins/plugins/administration/index.md | 2 +- .../administration-module-lifecycle.md | 4 ++-- guides/plugins/plugins/framework/store-api/index.md | 2 +- .../framework/store-api/store-api-route-lifecycle.md | 4 ++-- guides/plugins/plugins/plugin-fundamentals/index.md | 4 +++- .../plugin-fundamentals/scheduled-task-lifecycle.md | 4 ++-- .../plugins/plugins/storefront/javascript/index.md | 2 +- .../storefront-javascript-plugin-lifecycle.md | 4 ++-- .../plugins/understanding-plugin-feature-wiring.md | 12 ++++++------ 9 files changed, 20 insertions(+), 18 deletions(-) diff --git a/guides/plugins/plugins/administration/index.md b/guides/plugins/plugins/administration/index.md index a708d2ce05..ed61600e97 100644 --- a/guides/plugins/plugins/administration/index.md +++ b/guides/plugins/plugins/administration/index.md @@ -19,7 +19,7 @@ Typical use cases include: * Injecting services * Customizing templates and styling -This section follows a practical development workflow. Start with registering a module and route, then build the components, connect data, and refine permissions and UI behavior. +This section follows a practical development workflow. Start with registering a module and route, then build the components, connect data, and refine permissions and UI behavior. The [Administration Module Lifecycle](module-component-management/administration-module-lifecycle.md) explains how the entry point, module registration, routes, components, snippets, build, and UI discovery connect. For stable cross-version extension points, use the [Meteor Admin SDK](../../apps/administration/meteor-admin-sdk.md). The SDK can be used by both apps and plugins. diff --git a/guides/plugins/plugins/administration/module-component-management/administration-module-lifecycle.md b/guides/plugins/plugins/administration/module-component-management/administration-module-lifecycle.md index b3389d0c0b..064bff4194 100644 --- a/guides/plugins/plugins/administration/module-component-management/administration-module-lifecycle.md +++ b/guides/plugins/plugins/administration/module-component-management/administration-module-lifecycle.md @@ -1,10 +1,10 @@ --- nav: - title: Administration module lifecycle + title: Administration Module Lifecycle position: 15 --- -# Administration module lifecycle +# Administration Module Lifecycle An Administration module connects an entry point, module registration, routes, page components or templates, snippets, the Administration build, and the UI. diff --git a/guides/plugins/plugins/framework/store-api/index.md b/guides/plugins/plugins/framework/store-api/index.md index 7e7aae99c4..caa4bc537d 100644 --- a/guides/plugins/plugins/framework/store-api/index.md +++ b/guides/plugins/plugins/framework/store-api/index.md @@ -35,4 +35,4 @@ Storefront integration: ## Next steps -Review our guides for [adding routes](add-store-api-route.md) and [overriding existing routes](override-existing-route.md). +Review our guides for [adding routes](add-store-api-route.md), understanding the [Store API Route Lifecycle](store-api-route-lifecycle.md), and [overriding existing routes](override-existing-route.md). diff --git a/guides/plugins/plugins/framework/store-api/store-api-route-lifecycle.md b/guides/plugins/plugins/framework/store-api/store-api-route-lifecycle.md index cd3821afa0..00f7056a26 100644 --- a/guides/plugins/plugins/framework/store-api/store-api-route-lifecycle.md +++ b/guides/plugins/plugins/framework/store-api/store-api-route-lifecycle.md @@ -1,10 +1,10 @@ --- nav: - title: Store API route lifecycle + title: Store API Route Lifecycle position: 15 --- -# Store API route lifecycle +# Store API Route Lifecycle A Store API route crosses several independent boundaries: diff --git a/guides/plugins/plugins/plugin-fundamentals/index.md b/guides/plugins/plugins/plugin-fundamentals/index.md index a01ec59b6e..9846384a2f 100644 --- a/guides/plugins/plugins/plugin-fundamentals/index.md +++ b/guides/plugins/plugins/plugin-fundamentals/index.md @@ -9,12 +9,14 @@ nav: Plugin fundamentals are the building blocks for adding behavior to a Shopware plugin. Use this page to jump directly to the task you want to solve. +If you are adding generated or manually created components to an existing plugin, [Understanding Plugin Feature Wiring](../understanding-plugin-feature-wiring.md) explains how source files, framework registration, discovery, build/cache, and runtime fit together. + ## What do you want to do? - Run code when a plugin is installed, updated, activated, deactivated, or uninstalled: [Plugin lifecycle](plugin-lifecycle.md) - Add configurable settings that appear in the Administration: [Plugin configuration](add-plugin-configuration.md) - Add a custom Symfony console command: [CLI commands](add-custom-commands.md) -- Run recurring background work: [Scheduled tasks](add-scheduled-task.md) +- Run recurring background work: [Scheduled tasks](add-scheduled-task.md) and understand the [Scheduled Task Lifecycle](scheduled-task-lifecycle.md) - Add diagnostics and write plugin logs: [Logging](logging.md) - Register services or inject dependencies: [Services and dependency injection](../services/index.md) - React to Shopware events: [Listening to events](../framework/event/listening-to-events.md) diff --git a/guides/plugins/plugins/plugin-fundamentals/scheduled-task-lifecycle.md b/guides/plugins/plugins/plugin-fundamentals/scheduled-task-lifecycle.md index 027870a0e0..0b7f780f19 100644 --- a/guides/plugins/plugins/plugin-fundamentals/scheduled-task-lifecycle.md +++ b/guides/plugins/plugins/plugin-fundamentals/scheduled-task-lifecycle.md @@ -1,10 +1,10 @@ --- nav: - title: Scheduled task lifecycle + title: Scheduled Task Lifecycle position: 105 --- -# Scheduled task lifecycle +# Scheduled Task Lifecycle A scheduled task must be discovered, registered, persisted, scheduled, and eventually executed through Shopware's task runner and message queue. diff --git a/guides/plugins/plugins/storefront/javascript/index.md b/guides/plugins/plugins/storefront/javascript/index.md index ec37590b08..10f57a6b17 100644 --- a/guides/plugins/plugins/storefront/javascript/index.md +++ b/guides/plugins/plugins/storefront/javascript/index.md @@ -9,7 +9,7 @@ nav: This section explains how to extend and customize the Storefront using JavaScript plugins. It covers creating custom plugins, understanding how registration and DOM initialization fit together, overriding existing functionality, reacting to events, loading external scripts, and interacting with the Store API. * [Add Custom JavaScript](./add-custom-javascript.md) -* [Storefront JavaScript plugin lifecycle](./storefront-javascript-plugin-lifecycle.md) +* [Storefront JavaScript Plugin Lifecycle](./storefront-javascript-plugin-lifecycle.md) * [Add JavaScript as Script Tag](./add-javascript-as-script-tag.md) * [Fetching Data with JavaScript](./fetching-data-with-javascript.md) * [Override Existing JavaScript](./override-existing-javascript.md) diff --git a/guides/plugins/plugins/storefront/javascript/storefront-javascript-plugin-lifecycle.md b/guides/plugins/plugins/storefront/javascript/storefront-javascript-plugin-lifecycle.md index 219e2db035..ed684577ad 100644 --- a/guides/plugins/plugins/storefront/javascript/storefront-javascript-plugin-lifecycle.md +++ b/guides/plugins/plugins/storefront/javascript/storefront-javascript-plugin-lifecycle.md @@ -1,10 +1,10 @@ --- nav: - title: Storefront JavaScript plugin lifecycle + title: Storefront JavaScript Plugin Lifecycle position: 45 --- -# Storefront JavaScript plugin lifecycle +# Storefront JavaScript Plugin Lifecycle A Storefront JavaScript plugin connects a plugin class, the Storefront entry point, `PluginManager`, a DOM selector, a Twig hook, the asset build, and runtime initialization. diff --git a/guides/plugins/plugins/understanding-plugin-feature-wiring.md b/guides/plugins/plugins/understanding-plugin-feature-wiring.md index 064a8f97d2..6e715e2f81 100644 --- a/guides/plugins/plugins/understanding-plugin-feature-wiring.md +++ b/guides/plugins/plugins/understanding-plugin-feature-wiring.md @@ -1,10 +1,10 @@ --- nav: - title: Understanding plugin feature wiring + title: Understanding Plugin Feature Wiring position: 25 --- -# Understanding plugin feature wiring +# Understanding Plugin Feature Wiring A Shopware plugin feature is usually a chain of coordinated pieces rather than one file. Understanding that chain makes generated code easier to adapt and failures much faster to localize. @@ -70,10 +70,10 @@ Build commands compile assets. Cache clears refresh runtime-discovered configura The following pages explain how the pieces of larger extension points connect from source to runtime: -- [Storefront JavaScript plugin lifecycle](./storefront/javascript/storefront-javascript-plugin-lifecycle.md) -- [Administration module lifecycle](./administration/module-component-management/administration-module-lifecycle.md) -- [Store API route lifecycle](./framework/store-api/store-api-route-lifecycle.md) -- [Scheduled task lifecycle](./plugin-fundamentals/scheduled-task-lifecycle.md) +- [Storefront JavaScript Plugin Lifecycle](./storefront/javascript/storefront-javascript-plugin-lifecycle.md) +- [Administration Module Lifecycle](./administration/module-component-management/administration-module-lifecycle.md) +- [Store API Route Lifecycle](./framework/store-api/store-api-route-lifecycle.md) +- [Scheduled Task Lifecycle](./plugin-fundamentals/scheduled-task-lifecycle.md) Each lifecycle page links to the corresponding implementation guide and includes troubleshooting at the boundaries where failures commonly occur. From ce83ba87752da7a283c9f6496a6bed8a5d951bc3 Mon Sep 17 00:00:00 2001 From: LApple Date: Fri, 21 Aug 2026 18:19:05 +0200 Subject: [PATCH 07/26] docs: add Storefront controller lifecycle and plugin feature wiring docs Adds Storefront controller lifecycle guide explaining how controller class, route attributes, routing import, service registration, and cache connect. Warns about generated plugin base class name mismatches when plugin name does not follow UpperCamelCase, which can cause PluginBaseClassNotFoundException during installation even when plugin:refresh succeeds. Links lifecycle guides from index pages and remaining documentation. Aligns all lifecycle pages with repo formatting conventions (frontmatter blank lines, consistent command formatting, improved phrasing). Co-Authored-By: Claude Haiku 4.5 --- .../administration-module-lifecycle.md | 5 +- .../module-component-management/index.md | 1 + guides/plugins/plugins/creating-plugins.md | 16 ++++++ .../store-api/store-api-route-lifecycle.md | 1 + guides/plugins/plugins/index.md | 1 + guides/plugins/plugins/plugin-base-guide.md | 2 +- .../scheduled-task-lifecycle.md | 1 + .../plugins/storefront/controllers/index.md | 4 ++ .../storefront-controller-lifecycle.md | 51 +++++++++++++++++++ guides/plugins/plugins/storefront/index.md | 2 + .../storefront-javascript-plugin-lifecycle.md | 5 +- .../understanding-plugin-feature-wiring.md | 6 +++ 12 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 guides/plugins/plugins/storefront/controllers/storefront-controller-lifecycle.md diff --git a/guides/plugins/plugins/administration/module-component-management/administration-module-lifecycle.md b/guides/plugins/plugins/administration/module-component-management/administration-module-lifecycle.md index 064bff4194..3e684ed578 100644 --- a/guides/plugins/plugins/administration/module-component-management/administration-module-lifecycle.md +++ b/guides/plugins/plugins/administration/module-component-management/administration-module-lifecycle.md @@ -2,6 +2,7 @@ nav: title: Administration Module Lifecycle position: 15 + --- # Administration Module Lifecycle @@ -30,8 +31,8 @@ A successful build proves compilation. After rebuilding, refresh the Administrat ## Troubleshooting by boundary -If the build succeeds but the module is missing, inspect the `main.js` import, module registration, navigation path, and snippets before changing the page component. +When the build succeeds and the module is still missing, inspect the `main.js` import, module registration, navigation path, and snippets before changing the page component. -If the module appears but its route fails, inspect route-to-component wiring. +When the module appears and its route fails, inspect route-to-component wiring. When adding generated code to an existing plugin, preserve existing imports, routes, snippets, and unrelated modules rather than replacing the module graph. diff --git a/guides/plugins/plugins/administration/module-component-management/index.md b/guides/plugins/plugins/administration/module-component-management/index.md index 2229f6bd7e..0a1a028cac 100644 --- a/guides/plugins/plugins/administration/module-component-management/index.md +++ b/guides/plugins/plugins/administration/module-component-management/index.md @@ -11,6 +11,7 @@ This guide covers how to create, extend, and customize Administration modules an * [Add Custom Fields](add-custom-field.md) * [Add Custom Components](add-custom-component.md) * [Add Custom Modules](add-custom-module.md) +* [Administration Module Lifecycle](administration-module-lifecycle.md) * [Customize Components](customizing-components.md) * [Customize Modules](customizing-modules.md) * [Use Base Components](using-base-components.md) diff --git a/guides/plugins/plugins/creating-plugins.md b/guides/plugins/plugins/creating-plugins.md index bd7edc29d8..27ff28f0dc 100644 --- a/guides/plugins/plugins/creating-plugins.md +++ b/guides/plugins/plugins/creating-plugins.md @@ -105,6 +105,11 @@ related files and service definitions; deleting only one file later can leave br references or an invalid service configuration. If you are unsure whether you need an option, use `--no-scaffold` and add the feature from its focused guide instead. +When a generated feature does not behave as expected, [Understanding Plugin Feature +Wiring](./understanding-plugin-feature-wiring.md) explains how source files, framework +registration, discovery, build/cache, and runtime fit together, and links to focused +lifecycle guides for the larger extension points. + ::: info Generated files are tied to the Shopware version you run the command on. When your plugin supports several Shopware versions, treat the output as a starting point and verify it against the version you target. ::: @@ -193,6 +198,17 @@ At a minimum, it must define: The `extra.shopware-plugin-class` value must reference your plugin’s base PHP class (e.g. `Swag\\BasicExample\\SwagBasicExample`). +::: warning +`bin/console plugin:create` reporting success does not prove the plugin is installable. +If the plugin name and namespace do not follow `UpperCamelCase`, the generated +`extra.shopware-plugin-class` can reference a class that does not exist. The plugin is +still discovered by `bin/console plugin:refresh`, but `bin/console plugin:install` then +fails with `PluginBaseClassNotFoundException`. Check that +`extra.shopware-plugin-class` matches the PSR-4 namespace and the base class file name +before installing, and run `shopware-cli extension validate` to catch the remaining +metadata gaps. +::: + `shopware-platform-plugin` is the only Composer type Shopware treats as a plugin. A package with any other type — `library`, `project`, or the `shopware-app` type used for apps — is not picked up as a plugin and never appears in the Administration, even if everything else is set up correctly. Shopware also rejects a plugin whose `extra.shopware-plugin-class` or `extra.label` is missing. The `autoload.psr-4` namespace must match your directory structure. If you change the path (for example, not using `src/`), your folders must reflect that configuration. diff --git a/guides/plugins/plugins/framework/store-api/store-api-route-lifecycle.md b/guides/plugins/plugins/framework/store-api/store-api-route-lifecycle.md index 00f7056a26..b95f3c13df 100644 --- a/guides/plugins/plugins/framework/store-api/store-api-route-lifecycle.md +++ b/guides/plugins/plugins/framework/store-api/store-api-route-lifecycle.md @@ -2,6 +2,7 @@ nav: title: Store API Route Lifecycle position: 15 + --- # Store API Route Lifecycle diff --git a/guides/plugins/plugins/index.md b/guides/plugins/plugins/index.md index e0c6f640a6..55217adfd1 100644 --- a/guides/plugins/plugins/index.md +++ b/guides/plugins/plugins/index.md @@ -132,4 +132,5 @@ It is perfectly valid to ship multiple separate plugins, but keeping them in a s ## Next steps * Review the [Plugin base guide](./plugin-base-guide.md) to learn how to create plugins +* Read [Understanding Plugin Feature Wiring](./understanding-plugin-feature-wiring.md) to see how a plugin feature connects from source files to runtime * Make note of [CI](../../development/testing/ci.md) and other testing guidance to prevent upgrade-related regressions diff --git a/guides/plugins/plugins/plugin-base-guide.md b/guides/plugins/plugins/plugin-base-guide.md index b9729d3d02..dcd4f4a999 100644 --- a/guides/plugins/plugins/plugin-base-guide.md +++ b/guides/plugins/plugins/plugin-base-guide.md @@ -28,7 +28,7 @@ This guide outlines the typical development flow when creating a Shopware plugin Most steps above can be generated instead of written by hand: -* `bin/console plugin:create` scaffolds the plugin; see the [Creating Plugins guide](creating-plugins.md) +* `bin/console plugin:create` scaffolds the plugin; see the [Creating Plugins guide](creating-plugins.md); [Understanding Plugin Feature Wiring](understanding-plugin-feature-wiring.md) explains how generated pieces connect * The [Shopware 6 Toolbox plugin](../../development/tooling/shopware-toolbox.md) generates plugins, subscribers, scheduled tasks, migrations, and Administration modules from PHPStorm, using file templates you can adapt to your own conventions ## Upgrade readiness diff --git a/guides/plugins/plugins/plugin-fundamentals/scheduled-task-lifecycle.md b/guides/plugins/plugins/plugin-fundamentals/scheduled-task-lifecycle.md index 0b7f780f19..13f41fbac8 100644 --- a/guides/plugins/plugins/plugin-fundamentals/scheduled-task-lifecycle.md +++ b/guides/plugins/plugins/plugin-fundamentals/scheduled-task-lifecycle.md @@ -2,6 +2,7 @@ nav: title: Scheduled Task Lifecycle position: 105 + --- # Scheduled Task Lifecycle diff --git a/guides/plugins/plugins/storefront/controllers/index.md b/guides/plugins/plugins/storefront/controllers/index.md index 9bdd7b359d..cb60967482 100644 --- a/guides/plugins/plugins/storefront/controllers/index.md +++ b/guides/plugins/plugins/storefront/controllers/index.md @@ -50,3 +50,7 @@ Caching: * Use Symfony flash bags for error reporting. * Storefront functionality should be available inside the Store API too. + +## Next steps + +For implementation, see [Add Custom Controller](add-custom-controller.md). The [Storefront Controller Lifecycle](storefront-controller-lifecycle.md) explains how the controller class, route attributes, routing import, service registration, and cache connect, and where a controller can exist without being reachable. diff --git a/guides/plugins/plugins/storefront/controllers/storefront-controller-lifecycle.md b/guides/plugins/plugins/storefront/controllers/storefront-controller-lifecycle.md new file mode 100644 index 0000000000..ec86d77cec --- /dev/null +++ b/guides/plugins/plugins/storefront/controllers/storefront-controller-lifecycle.md @@ -0,0 +1,51 @@ +--- +nav: + title: Storefront Controller Lifecycle + position: 30 + +--- + +# Storefront Controller Lifecycle + +A Storefront controller becomes a working URL only after several independent pieces agree: the controller class, its route attributes, a routing import, a service definition, and a cleared container. + +```text +controller class â route attributes â routes.php import â service registration â router â cache â request +``` + +For implementation, see [Add Custom Controller](./add-custom-controller.md). + +## What each stage proves + +A controller class on disk proves nothing about reachability. Shopware discovers Storefront routes through the plugin's routing configuration, and a freshly created plugin does not have one. Its `src/Resources/config` directory typically contains `services.php`, `services.xml`, and `config.xml`, but no `routes.php` until a generator or the developer adds it. + +Prove route discovery with the Symfony router: + +```bash +bin/console debug:router | grep your-route +``` + +A discovered route still does not prove that the controller can be constructed. Storefront controllers are services, so the route resolves but the request fails until the controller is registered in the plugin's service configuration. + +## Troubleshooting by boundary + +```text +route absent from debug:router â route attributes or missing routes.php import +route discovered, HTTP 500 with no container â controller not registered as a service +service registered, container error â missing service() import or wrong class reference +route reachable, wrong output â controller implementation or Twig template +``` + +Clear the cache after changing routing or service configuration: + +```bash +bin/console cache:clear +``` + +Each boundary produces a different symptom, so identify the failing boundary before changing controller code. A missing routing import and an unregistered service both look like "my controller does not work", but they fail at different stages and need different fixes. + +## Working with generated controllers + +The `--create-storefront-controller` scaffold generates the controller, its template, the service definition, and the `routes.php` entry together. When you add a controller by hand or copy one between plugins, check each of those four pieces separately rather than assuming the class is enough. + +When adding a controller to a plugin that already has routing configuration, extend the existing `routes.php` and service definitions rather than replacing them. diff --git a/guides/plugins/plugins/storefront/index.md b/guides/plugins/plugins/storefront/index.md index 4daef7317d..d00ea60f5f 100644 --- a/guides/plugins/plugins/storefront/index.md +++ b/guides/plugins/plugins/storefront/index.md @@ -45,6 +45,7 @@ Infrastructure and optimization topics. Create new routes and pages, or extend existing ones. * [Add custom controller](../storefront/controllers/add-custom-controller.md) +* [Storefront Controller Lifecycle](../storefront/controllers/storefront-controller-lifecycle.md) * [Add custom page](../storefront/controllers/add-custom-page.md) * [Add custom pagelet](../storefront/controllers/add-custom-pagelet.md) * [Add data to a storefront page](../storefront/controllers/add-data-to-storefront-page.md) @@ -68,6 +69,7 @@ Feature-specific examples and focused use-cases. Extend or override frontend behavior. * [Add custom JavaScript](../storefront/javascript/add-custom-javascript.md) +* [Storefront JavaScript Plugin Lifecycle](../storefront/javascript/storefront-javascript-plugin-lifecycle.md) * [Add JavaScript as script tag](../storefront/javascript/add-javascript-as-script-tag.md) * [Fetch data dynamically](../storefront/javascript/fetching-data-with-javascript.md) * [Override existing JavaScript](../storefront/javascript/override-existing-javascript.md) diff --git a/guides/plugins/plugins/storefront/javascript/storefront-javascript-plugin-lifecycle.md b/guides/plugins/plugins/storefront/javascript/storefront-javascript-plugin-lifecycle.md index ed684577ad..debaba145b 100644 --- a/guides/plugins/plugins/storefront/javascript/storefront-javascript-plugin-lifecycle.md +++ b/guides/plugins/plugins/storefront/javascript/storefront-javascript-plugin-lifecycle.md @@ -1,7 +1,8 @@ --- nav: title: Storefront JavaScript Plugin Lifecycle - position: 45 + position: 55 + --- # Storefront JavaScript Plugin Lifecycle @@ -55,7 +56,7 @@ shopware-cli project storefront-build When Twig changed, also clear Shopware's cache: ```bash -shopware-cli project console cache:clear +bin/console cache:clear ``` Then verify the rendered HTML or DOM before debugging the class: diff --git a/guides/plugins/plugins/understanding-plugin-feature-wiring.md b/guides/plugins/plugins/understanding-plugin-feature-wiring.md index 6e715e2f81..a8fed0c2ab 100644 --- a/guides/plugins/plugins/understanding-plugin-feature-wiring.md +++ b/guides/plugins/plugins/understanding-plugin-feature-wiring.md @@ -2,6 +2,7 @@ nav: title: Understanding Plugin Feature Wiring position: 25 + --- # Understanding Plugin Feature Wiring @@ -54,10 +55,14 @@ When a class is not found, compare the Composer PSR-4 prefix, PHP namespace, and Many plugin features are services. Tags such as `kernel.event_subscriber`, `console.command`, and `shopware.scheduled.task` tell Symfony and Shopware how the service participates in the framework. +Service configuration is a separate boundary from the class itself. A correct PHP class whose service definition references a different class name fails at registration, and Shopware may only surface that mismatch later, when the plugin is activated. + ### Routes Use `debug:router` to prove route discovery, then make a real request to prove reachability. A missing route, container-construction error, authentication response, and endpoint exception are different failure boundaries. +A plugin does not have routing configuration until something adds it. A controller class can be present and correct while its route is undiscoverable because no `routes.php` imports it. + ### Configuration A valid `config.xml` proves structural validity. The stronger user-facing check is that the field appears and behaves correctly in the Administration. @@ -70,6 +75,7 @@ Build commands compile assets. Cache clears refresh runtime-discovered configura The following pages explain how the pieces of larger extension points connect from source to runtime: +- [Storefront Controller Lifecycle](./storefront/controllers/storefront-controller-lifecycle.md) - [Storefront JavaScript Plugin Lifecycle](./storefront/javascript/storefront-javascript-plugin-lifecycle.md) - [Administration Module Lifecycle](./administration/module-component-management/administration-module-lifecycle.md) - [Store API Route Lifecycle](./framework/store-api/store-api-route-lifecycle.md) From 496e6fa5998d86a52951298bf16e74797e67718c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Aug 2026 16:32:04 +0000 Subject: [PATCH 08/26] docs: replace misspelled words with alternatives Co-authored-by: lasomethingsomething <2453640+lasomethingsomething@users.noreply.github.com> --- guides/plugins/plugins/understanding-plugin-feature-wiring.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guides/plugins/plugins/understanding-plugin-feature-wiring.md b/guides/plugins/plugins/understanding-plugin-feature-wiring.md index a8fed0c2ab..2676cc9050 100644 --- a/guides/plugins/plugins/understanding-plugin-feature-wiring.md +++ b/guides/plugins/plugins/understanding-plugin-feature-wiring.md @@ -59,9 +59,9 @@ Service configuration is a separate boundary from the class itself. A correct PH ### Routes -Use `debug:router` to prove route discovery, then make a real request to prove reachability. A missing route, container-construction error, authentication response, and endpoint exception are different failure boundaries. +Use `debug:router` to prove route discovery, then make a real request to verify the route is accessible. A missing route, container-construction error, authentication response, and endpoint exception are different failure boundaries. -A plugin does not have routing configuration until something adds it. A controller class can be present and correct while its route is undiscoverable because no `routes.php` imports it. +A plugin does not have routing configuration until something adds it. A controller class can be present and correct while its route is invisible because no `routes.php` imports it. ### Configuration From d568a44abdf715426ee0f1dc9a85dc6785ae1513 Mon Sep 17 00:00:00 2001 From: somethings Date: Fri, 21 Aug 2026 18:34:54 +0200 Subject: [PATCH 09/26] docs: fix controller lifecycle spellcheck text --- .../controllers/storefront-controller-lifecycle.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/guides/plugins/plugins/storefront/controllers/storefront-controller-lifecycle.md b/guides/plugins/plugins/storefront/controllers/storefront-controller-lifecycle.md index ec86d77cec..8ddfb7ddb0 100644 --- a/guides/plugins/plugins/storefront/controllers/storefront-controller-lifecycle.md +++ b/guides/plugins/plugins/storefront/controllers/storefront-controller-lifecycle.md @@ -10,7 +10,7 @@ nav: A Storefront controller becomes a working URL only after several independent pieces agree: the controller class, its route attributes, a routing import, a service definition, and a cleared container. ```text -controller class â route attributes â routes.php import â service registration â router â cache â request +controller class → route attributes → routes.php import → service registration → router → cache → request ``` For implementation, see [Add Custom Controller](./add-custom-controller.md). @@ -30,10 +30,10 @@ A discovered route still does not prove that the controller can be constructed. ## Troubleshooting by boundary ```text -route absent from debug:router â route attributes or missing routes.php import -route discovered, HTTP 500 with no container â controller not registered as a service -service registered, container error â missing service() import or wrong class reference -route reachable, wrong output â controller implementation or Twig template +route absent from debug:router → route attributes or missing routes.php import +route discovered, HTTP 500 with no container → controller not registered as a service +service registered, container error → missing service() import or wrong class reference +route reachable, wrong output → controller implementation or Twig template ``` Clear the cache after changing routing or service configuration: From 9d20fffa41c6302ba594175c09937344b9eec354 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 12:11:17 +0200 Subject: [PATCH 10/26] Update storefront-controller-lifecycle.md --- .../storefront/controllers/storefront-controller-lifecycle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/plugins/plugins/storefront/controllers/storefront-controller-lifecycle.md b/guides/plugins/plugins/storefront/controllers/storefront-controller-lifecycle.md index 8ddfb7ddb0..be891d002c 100644 --- a/guides/plugins/plugins/storefront/controllers/storefront-controller-lifecycle.md +++ b/guides/plugins/plugins/storefront/controllers/storefront-controller-lifecycle.md @@ -17,7 +17,7 @@ For implementation, see [Add Custom Controller](./add-custom-controller.md). ## What each stage proves -A controller class on disk proves nothing about reachability. Shopware discovers Storefront routes through the plugin's routing configuration, and a freshly created plugin does not have one. Its `src/Resources/config` directory typically contains `services.php`, `services.xml`, and `config.xml`, but no `routes.php` until a generator or the developer adds it. +A controller class on disk proves nothing about availability. Shopware discovers Storefront routes through the plugin's routing configuration, and a freshly created plugin does not have one. Its `src/Resources/config` directory typically contains `services.php`, `services.xml`, and `config.xml`, but no `routes.php` until a generator or the developer adds it. Prove route discovery with the Symfony router: From 1de5d526a3c03b60e12aeb30bb544ecef9f7ee75 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 12:42:55 +0200 Subject: [PATCH 11/26] docs: consolidate plugin wiring guidance --- .../plugins/plugins/administration/index.md | 2 +- .../add-custom-module.md | 12 +++ .../administration-module-lifecycle.md | 38 --------- .../module-component-management/index.md | 1 - guides/plugins/plugins/creating-plugins.md | 6 +- .../store-api/add-store-api-route.md | 12 +++ .../plugins/framework/store-api/index.md | 2 +- .../store-api/store-api-route-lifecycle.md | 41 ---------- .../plugin-fundamentals/add-scheduled-task.md | 12 +++ .../plugins/plugin-fundamentals/index.md | 2 +- .../scheduled-task-lifecycle.md | 37 --------- .../controllers/add-custom-controller.md | 12 +++ .../plugins/storefront/controllers/index.md | 2 +- .../storefront-controller-lifecycle.md | 51 ------------ guides/plugins/plugins/storefront/index.md | 2 - .../javascript/add-custom-javascript.md | 11 +++ .../plugins/storefront/javascript/index.md | 1 - .../storefront-javascript-plugin-lifecycle.md | 78 ------------------- .../understanding-plugin-feature-wiring.md | 16 ++-- 19 files changed, 74 insertions(+), 264 deletions(-) delete mode 100644 guides/plugins/plugins/administration/module-component-management/administration-module-lifecycle.md delete mode 100644 guides/plugins/plugins/framework/store-api/store-api-route-lifecycle.md delete mode 100644 guides/plugins/plugins/plugin-fundamentals/scheduled-task-lifecycle.md delete mode 100644 guides/plugins/plugins/storefront/controllers/storefront-controller-lifecycle.md delete mode 100644 guides/plugins/plugins/storefront/javascript/storefront-javascript-plugin-lifecycle.md diff --git a/guides/plugins/plugins/administration/index.md b/guides/plugins/plugins/administration/index.md index ed61600e97..a708d2ce05 100644 --- a/guides/plugins/plugins/administration/index.md +++ b/guides/plugins/plugins/administration/index.md @@ -19,7 +19,7 @@ Typical use cases include: * Injecting services * Customizing templates and styling -This section follows a practical development workflow. Start with registering a module and route, then build the components, connect data, and refine permissions and UI behavior. The [Administration Module Lifecycle](module-component-management/administration-module-lifecycle.md) explains how the entry point, module registration, routes, components, snippets, build, and UI discovery connect. +This section follows a practical development workflow. Start with registering a module and route, then build the components, connect data, and refine permissions and UI behavior. For stable cross-version extension points, use the [Meteor Admin SDK](../../apps/administration/meteor-admin-sdk.md). The SDK can be used by both apps and plugins. diff --git a/guides/plugins/plugins/administration/module-component-management/add-custom-module.md b/guides/plugins/plugins/administration/module-component-management/add-custom-module.md index f42b23d61a..e0005616b4 100644 --- a/guides/plugins/plugins/administration/module-component-management/add-custom-module.md +++ b/guides/plugins/plugins/administration/module-component-management/add-custom-module.md @@ -293,6 +293,18 @@ Shopware.Module.register('swag-example', { }); ``` +## Verify module discovery + +An Administration build proves compilation, not that the module is discoverable or navigable. Check the boundaries separately: + +1. Confirm that the plugin Administration `main.js` imports the module. +2. Confirm that the module is registered with `Shopware.Module.register()`. +3. Confirm that navigation paths resolve to registered module routes and components. +4. Confirm that labels and titles use existing snippet keys. +5. Run `shopware-cli project admin-build`, refresh the Administration, and open the initial route. + +If the build succeeds but the module is missing, inspect the entry-point import, module registration, navigation path, and snippets before changing the page component. Preserve existing imports, routes, snippets, and unrelated modules when adding generated code. + ## Next steps As you might have noticed, we are just adding a custom module to the module. diff --git a/guides/plugins/plugins/administration/module-component-management/administration-module-lifecycle.md b/guides/plugins/plugins/administration/module-component-management/administration-module-lifecycle.md deleted file mode 100644 index 3e684ed578..0000000000 --- a/guides/plugins/plugins/administration/module-component-management/administration-module-lifecycle.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -nav: - title: Administration Module Lifecycle - position: 15 - ---- - -# Administration Module Lifecycle - -An Administration module connects an entry point, module registration, routes, page components or templates, snippets, the Administration build, and the UI. - -```text -main.js → module import/registration → route → component/template → snippets → build → UI → initial route -``` - -For implementation, see [Add Custom Module](./add-custom-module.md). - -## What each stage proves - -The plugin Administration `main.js` must import the module. The module then registers through `Shopware.Module.register()`. A valid module file remains invisible if the entry point never imports it. - -Routes used by navigation must resolve to registered page components, and snippet keys used by labels and titles must exist. - -Build the Administration with: - -```bash -shopware-cli project admin-build -``` - -A successful build proves compilation. After rebuilding, refresh the Administration and separately verify that the module appears and that its initial route opens. - -## Troubleshooting by boundary - -When the build succeeds and the module is still missing, inspect the `main.js` import, module registration, navigation path, and snippets before changing the page component. - -When the module appears and its route fails, inspect route-to-component wiring. - -When adding generated code to an existing plugin, preserve existing imports, routes, snippets, and unrelated modules rather than replacing the module graph. diff --git a/guides/plugins/plugins/administration/module-component-management/index.md b/guides/plugins/plugins/administration/module-component-management/index.md index 0a1a028cac..2229f6bd7e 100644 --- a/guides/plugins/plugins/administration/module-component-management/index.md +++ b/guides/plugins/plugins/administration/module-component-management/index.md @@ -11,7 +11,6 @@ This guide covers how to create, extend, and customize Administration modules an * [Add Custom Fields](add-custom-field.md) * [Add Custom Components](add-custom-component.md) * [Add Custom Modules](add-custom-module.md) -* [Administration Module Lifecycle](administration-module-lifecycle.md) * [Customize Components](customizing-components.md) * [Customize Modules](customizing-modules.md) * [Use Base Components](using-base-components.md) diff --git a/guides/plugins/plugins/creating-plugins.md b/guides/plugins/plugins/creating-plugins.md index 27ff28f0dc..16984f79ba 100644 --- a/guides/plugins/plugins/creating-plugins.md +++ b/guides/plugins/plugins/creating-plugins.md @@ -106,9 +106,9 @@ references or an invalid service configuration. If you are unsure whether you ne an option, use `--no-scaffold` and add the feature from its focused guide instead. When a generated feature does not behave as expected, [Understanding Plugin Feature -Wiring](./understanding-plugin-feature-wiring.md) explains how source files, framework -registration, discovery, build/cache, and runtime fit together, and links to focused -lifecycle guides for the larger extension points. +Wiring](./understanding-plugin-feature-wiring.md) explains the shared boundaries to +check. The focused implementation guides contain the feature-specific verification +steps. ::: info Generated files are tied to the Shopware version you run the command on. When your plugin supports several Shopware versions, treat the output as a starting point and verify it against the version you target. diff --git a/guides/plugins/plugins/framework/store-api/add-store-api-route.md b/guides/plugins/plugins/framework/store-api/add-store-api-route.md index 447b14100f..47f2f8ed28 100644 --- a/guides/plugins/plugins/framework/store-api/add-store-api-route.md +++ b/guides/plugins/plugins/framework/store-api/add-store-api-route.md @@ -408,3 +408,15 @@ export default class ExamplePlugin extends PluginBaseClass { } } ``` + +## Verify route discovery and execution + +A route class on disk does not prove that the route is usable. Check the boundaries separately: + +1. Confirm the route attribute uses the Store API scope and that the route class is registered as a service. +2. Confirm that `src/Resources/config/routes.php` imports the route. +3. Run `bin/console debug:router store-api.example.search` (or your route name) to prove discovery. +4. Make an authenticated Store API request to verify execution. + +A response requiring `sw-access-key` means that routing reached Store API authentication; it is not evidence that the route is missing. Treat route absence, container errors, authentication responses, and endpoint-specific errors as different failure boundaries. + diff --git a/guides/plugins/plugins/framework/store-api/index.md b/guides/plugins/plugins/framework/store-api/index.md index caa4bc537d..7e7aae99c4 100644 --- a/guides/plugins/plugins/framework/store-api/index.md +++ b/guides/plugins/plugins/framework/store-api/index.md @@ -35,4 +35,4 @@ Storefront integration: ## Next steps -Review our guides for [adding routes](add-store-api-route.md), understanding the [Store API Route Lifecycle](store-api-route-lifecycle.md), and [overriding existing routes](override-existing-route.md). +Review our guides for [adding routes](add-store-api-route.md) and [overriding existing routes](override-existing-route.md). diff --git a/guides/plugins/plugins/framework/store-api/store-api-route-lifecycle.md b/guides/plugins/plugins/framework/store-api/store-api-route-lifecycle.md deleted file mode 100644 index b95f3c13df..0000000000 --- a/guides/plugins/plugins/framework/store-api/store-api-route-lifecycle.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -nav: - title: Store API Route Lifecycle - position: 15 - ---- - -# Store API Route Lifecycle - -A Store API route crosses several independent boundaries: - -```text -route/response classes → routes.php import → service registration → router → authentication/context → execution -``` - -For implementation, see [Add Store API Route](./add-store-api-route.md). - -## Discovery - -Use the Symfony router to prove Shopware discovered the route: - -```bash -bin/console debug:router | grep your-route -``` - -A route appearing here does not prove that its service can be constructed or that a Store API request can execute. - -## Authentication and execution - -Make a real Store API request to test the next boundary. A response requiring `sw-access-key` means the request reached Store API authentication; it is not evidence that the route is missing. - -```text -404 / route absent → route attributes or routes.php import -container error → service registration or dependencies -401 requiring sw-access-key → routing succeeded; authentication is now the boundary -endpoint-specific response/error → route implementation -``` - -Admin API and Store API helpers use different authentication models, so do not substitute one for the other when verifying a Store API route. - -Generated PHP files are therefore only the first check. Verify router discovery and a real authenticated request as separate acceptance steps. diff --git a/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md b/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md index 9a5e612d5a..cf3238ecea 100644 --- a/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md +++ b/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md @@ -162,6 +162,18 @@ Now you still need to run the command `bin/console messenger:consume` to actuall +## Verify the complete task lifecycle + +A scheduled task needs both the task definition and its handler. Check the boundaries separately: + +1. Confirm that the task is tagged with `shopware.scheduled.task`. +2. Confirm that the handler is registered with `messenger.message_handler` and handles the task message. +3. Run `bin/console scheduled-task:register`, then use `bin/console scheduled-task:list | grep your-task-name` to confirm the persisted task. +4. Run `bin/console scheduled-task:run` to dispatch due tasks. +5. Run `bin/console messenger:consume` (unless the Administration worker is handling the queue) and verify that the handler executes. + +A task appearing in `scheduled-task:list` proves registration and persistence, not execution. If it is registered but does not run, check its status, schedule, handler registration, runner, and message queue separately. + ## Next steps [Adding a custom command](add-custom-commands.md) diff --git a/guides/plugins/plugins/plugin-fundamentals/index.md b/guides/plugins/plugins/plugin-fundamentals/index.md index 9846384a2f..c37879b156 100644 --- a/guides/plugins/plugins/plugin-fundamentals/index.md +++ b/guides/plugins/plugins/plugin-fundamentals/index.md @@ -16,7 +16,7 @@ If you are adding generated or manually created components to an existing plugin - Run code when a plugin is installed, updated, activated, deactivated, or uninstalled: [Plugin lifecycle](plugin-lifecycle.md) - Add configurable settings that appear in the Administration: [Plugin configuration](add-plugin-configuration.md) - Add a custom Symfony console command: [CLI commands](add-custom-commands.md) -- Run recurring background work: [Scheduled tasks](add-scheduled-task.md) and understand the [Scheduled Task Lifecycle](scheduled-task-lifecycle.md) +- Run recurring background work: [Scheduled tasks](add-scheduled-task.md) - Add diagnostics and write plugin logs: [Logging](logging.md) - Register services or inject dependencies: [Services and dependency injection](../services/index.md) - React to Shopware events: [Listening to events](../framework/event/listening-to-events.md) diff --git a/guides/plugins/plugins/plugin-fundamentals/scheduled-task-lifecycle.md b/guides/plugins/plugins/plugin-fundamentals/scheduled-task-lifecycle.md deleted file mode 100644 index 13f41fbac8..0000000000 --- a/guides/plugins/plugins/plugin-fundamentals/scheduled-task-lifecycle.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -nav: - title: Scheduled Task Lifecycle - position: 105 - ---- - -# Scheduled Task Lifecycle - -A scheduled task must be discovered, registered, persisted, scheduled, and eventually executed through Shopware's task runner and message queue. - -For the broader implementation, including handlers and execution, see [Add Scheduled Task](./add-scheduled-task.md). - -The current Core scaffolding example for a scheduled task creates the task class and its `shopware.scheduled.task` registration. Treat that focused scaffold separately from the complete application pattern described in the implementation guide, and verify conventions against the Shopware version you target. - -## Registration lifecycle - -```text -task class → services.php/tag → scheduled-task:register → scheduled-task:list → persisted schedule → runner/message queue → execution -``` - -Useful discovery checks are: - -```bash -bin/console scheduled-task:register -bin/console scheduled-task:list | grep your-task-name -``` - -## Troubleshooting by boundary - -```text -class-not-found → code / namespace / autoloading -task absent from scheduled-task:list → service registration / discovery -task registered but not executing → schedule state / runner / message queue / handler -``` - -Changing service registration can require a cache/container refresh. Re-running plugin activation for an already-active plugin does not by itself prove that the Symfony container was rebuilt. diff --git a/guides/plugins/plugins/storefront/controllers/add-custom-controller.md b/guides/plugins/plugins/storefront/controllers/add-custom-controller.md index 421d5d0b14..f2f0e0b6c5 100644 --- a/guides/plugins/plugins/storefront/controllers/add-custom-controller.md +++ b/guides/plugins/plugins/storefront/controllers/add-custom-controller.md @@ -311,6 +311,18 @@ public function showExample(Request $request, SalesChannelContext $context): Res } ``` +## Verify the feature wiring + +A controller class alone does not make a URL available. Verify the boundaries in order: + +1. Confirm that the controller has the expected route attributes. +2. Confirm that `src/Resources/config/routes.php` imports the controller. +3. Confirm that the controller is registered as a public service and that its container is injected. +4. Run `bin/console debug:router | grep your-route` to prove route discovery. +5. Clear the cache with `bin/console cache:clear`, then make a real request and check the response. + +A route appearing in `debug:router` proves discovery, not that the controller can be constructed. If a plugin already has routing or service configuration, extend the existing files instead of replacing them. + ## Next steps Since you've already created a controller now, which is also part of creating a so-called "page" in Shopware, you might want to head over to our guide about [creating a page](../controllers/add-custom-page.md). diff --git a/guides/plugins/plugins/storefront/controllers/index.md b/guides/plugins/plugins/storefront/controllers/index.md index cb60967482..11bca212ec 100644 --- a/guides/plugins/plugins/storefront/controllers/index.md +++ b/guides/plugins/plugins/storefront/controllers/index.md @@ -53,4 +53,4 @@ Caching: ## Next steps -For implementation, see [Add Custom Controller](add-custom-controller.md). The [Storefront Controller Lifecycle](storefront-controller-lifecycle.md) explains how the controller class, route attributes, routing import, service registration, and cache connect, and where a controller can exist without being reachable. +For implementation and troubleshooting, see [Add Custom Controller](add-custom-controller.md). diff --git a/guides/plugins/plugins/storefront/controllers/storefront-controller-lifecycle.md b/guides/plugins/plugins/storefront/controllers/storefront-controller-lifecycle.md deleted file mode 100644 index be891d002c..0000000000 --- a/guides/plugins/plugins/storefront/controllers/storefront-controller-lifecycle.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -nav: - title: Storefront Controller Lifecycle - position: 30 - ---- - -# Storefront Controller Lifecycle - -A Storefront controller becomes a working URL only after several independent pieces agree: the controller class, its route attributes, a routing import, a service definition, and a cleared container. - -```text -controller class → route attributes → routes.php import → service registration → router → cache → request -``` - -For implementation, see [Add Custom Controller](./add-custom-controller.md). - -## What each stage proves - -A controller class on disk proves nothing about availability. Shopware discovers Storefront routes through the plugin's routing configuration, and a freshly created plugin does not have one. Its `src/Resources/config` directory typically contains `services.php`, `services.xml`, and `config.xml`, but no `routes.php` until a generator or the developer adds it. - -Prove route discovery with the Symfony router: - -```bash -bin/console debug:router | grep your-route -``` - -A discovered route still does not prove that the controller can be constructed. Storefront controllers are services, so the route resolves but the request fails until the controller is registered in the plugin's service configuration. - -## Troubleshooting by boundary - -```text -route absent from debug:router → route attributes or missing routes.php import -route discovered, HTTP 500 with no container → controller not registered as a service -service registered, container error → missing service() import or wrong class reference -route reachable, wrong output → controller implementation or Twig template -``` - -Clear the cache after changing routing or service configuration: - -```bash -bin/console cache:clear -``` - -Each boundary produces a different symptom, so identify the failing boundary before changing controller code. A missing routing import and an unregistered service both look like "my controller does not work", but they fail at different stages and need different fixes. - -## Working with generated controllers - -The `--create-storefront-controller` scaffold generates the controller, its template, the service definition, and the `routes.php` entry together. When you add a controller by hand or copy one between plugins, check each of those four pieces separately rather than assuming the class is enough. - -When adding a controller to a plugin that already has routing configuration, extend the existing `routes.php` and service definitions rather than replacing them. diff --git a/guides/plugins/plugins/storefront/index.md b/guides/plugins/plugins/storefront/index.md index d00ea60f5f..4daef7317d 100644 --- a/guides/plugins/plugins/storefront/index.md +++ b/guides/plugins/plugins/storefront/index.md @@ -45,7 +45,6 @@ Infrastructure and optimization topics. Create new routes and pages, or extend existing ones. * [Add custom controller](../storefront/controllers/add-custom-controller.md) -* [Storefront Controller Lifecycle](../storefront/controllers/storefront-controller-lifecycle.md) * [Add custom page](../storefront/controllers/add-custom-page.md) * [Add custom pagelet](../storefront/controllers/add-custom-pagelet.md) * [Add data to a storefront page](../storefront/controllers/add-data-to-storefront-page.md) @@ -69,7 +68,6 @@ Feature-specific examples and focused use-cases. Extend or override frontend behavior. * [Add custom JavaScript](../storefront/javascript/add-custom-javascript.md) -* [Storefront JavaScript Plugin Lifecycle](../storefront/javascript/storefront-javascript-plugin-lifecycle.md) * [Add JavaScript as script tag](../storefront/javascript/add-javascript-as-script-tag.md) * [Fetch data dynamically](../storefront/javascript/fetching-data-with-javascript.md) * [Override existing JavaScript](../storefront/javascript/override-existing-javascript.md) diff --git a/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md b/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md index 77e7942e21..7bf4848526 100644 --- a/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md +++ b/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md @@ -266,6 +266,17 @@ composer run build:js:storefront If you now scroll to the bottom of your page an alert should appear. +## Verify registration and runtime + +An asset build proves compilation, not initialization. Check the boundaries separately: + +1. Confirm that the plugin class is imported and registered from `main.js`. +2. Confirm that the selector passed to `PluginManager.register()` is present in the rendered HTML. +3. Run the Storefront build and reload the page. +4. Check the browser console and use a temporary log in `init()` to prove runtime initialization. + +If the selector is absent, inspect the Twig template and cache. If the selector is present but `init()` does not run, inspect the entry-point import, registration, and the generated asset. + ## Next steps With your own first JavaScript plugin now running, you might want to start [listening to JavaScript events](./reacting-to-javascript-events.md) or [overriding other JavaScript plugins](./override-existing-javascript.md). diff --git a/guides/plugins/plugins/storefront/javascript/index.md b/guides/plugins/plugins/storefront/javascript/index.md index 10f57a6b17..e2be04a72a 100644 --- a/guides/plugins/plugins/storefront/javascript/index.md +++ b/guides/plugins/plugins/storefront/javascript/index.md @@ -9,7 +9,6 @@ nav: This section explains how to extend and customize the Storefront using JavaScript plugins. It covers creating custom plugins, understanding how registration and DOM initialization fit together, overriding existing functionality, reacting to events, loading external scripts, and interacting with the Store API. * [Add Custom JavaScript](./add-custom-javascript.md) -* [Storefront JavaScript Plugin Lifecycle](./storefront-javascript-plugin-lifecycle.md) * [Add JavaScript as Script Tag](./add-javascript-as-script-tag.md) * [Fetching Data with JavaScript](./fetching-data-with-javascript.md) * [Override Existing JavaScript](./override-existing-javascript.md) diff --git a/guides/plugins/plugins/storefront/javascript/storefront-javascript-plugin-lifecycle.md b/guides/plugins/plugins/storefront/javascript/storefront-javascript-plugin-lifecycle.md deleted file mode 100644 index debaba145b..0000000000 --- a/guides/plugins/plugins/storefront/javascript/storefront-javascript-plugin-lifecycle.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -nav: - title: Storefront JavaScript Plugin Lifecycle - position: 55 - ---- - -# Storefront JavaScript Plugin Lifecycle - -A Storefront JavaScript plugin connects a plugin class, the Storefront entry point, `PluginManager`, a DOM selector, a Twig hook, the asset build, and runtime initialization. - -```text -plugin class → main.js → PluginManager + selector → Twig target → build/cache → DOM → init() -``` - -For the step-by-step implementation, see [Add Custom JavaScript](./add-custom-javascript.md). - -## Base class and entry point - -The current Core scaffolding example imports the Storefront base class explicitly: - -```js -import Plugin from 'src/plugin-system/plugin.class'; - -export default class ExamplePlugin extends Plugin { - init() {} -} -``` - -A plausible alternative can compile and still fail at runtime. Use the convention supported by the Shopware version you target. - -Register the plugin from `main.js` with a selector when it should initialize on specific elements: - -```js -import ExamplePlugin from './example-plugin/example-plugin.plugin'; - -window.PluginManager.register('ExamplePlugin', ExamplePlugin, '[data-example-plugin]'); -``` - -## Template target - -The selector is part of the feature contract. The page must render a matching target, for example: - -```twig - -``` - -## Build, cache, and runtime - -Build assets with: - -```bash -shopware-cli project storefront-build -``` - -When Twig changed, also clear Shopware's cache: - -```bash -bin/console cache:clear -``` - -Then verify the rendered HTML or DOM before debugging the class: - -```js -document.querySelector('[data-example-plugin]') -``` - -A successful asset build proves compilation, not initialization. - -## Troubleshooting by boundary - -```text -selector absent from HTML → template inheritance / cache / tested page -selector present, no registration → main.js / PluginManager -registration present, init error → plugin class / Storefront API / version mismatch -``` - -A temporary `console.log()` in `init()` is a simple final runtime check during development. diff --git a/guides/plugins/plugins/understanding-plugin-feature-wiring.md b/guides/plugins/plugins/understanding-plugin-feature-wiring.md index 2676cc9050..ecd21c2e58 100644 --- a/guides/plugins/plugins/understanding-plugin-feature-wiring.md +++ b/guides/plugins/plugins/understanding-plugin-feature-wiring.md @@ -71,17 +71,17 @@ A valid `config.xml` proves structural validity. The stronger user-facing check Build commands compile assets. Cache clears refresh runtime-discovered configuration and templates. They are related but not interchangeable; for example, a successful Storefront build does not itself guarantee that changed Twig output is rendered. -## Focused lifecycle guides +## Focused implementation guides -The following pages explain how the pieces of larger extension points connect from source to runtime: +Use the feature-specific guides for implementation details and verification steps: -- [Storefront Controller Lifecycle](./storefront/controllers/storefront-controller-lifecycle.md) -- [Storefront JavaScript Plugin Lifecycle](./storefront/javascript/storefront-javascript-plugin-lifecycle.md) -- [Administration Module Lifecycle](./administration/module-component-management/administration-module-lifecycle.md) -- [Store API Route Lifecycle](./framework/store-api/store-api-route-lifecycle.md) -- [Scheduled Task Lifecycle](./plugin-fundamentals/scheduled-task-lifecycle.md) +- [Add Custom Controller](./storefront/controllers/add-custom-controller.md) +- [Add Custom JavaScript](./storefront/javascript/add-custom-javascript.md) +- [Add Custom Module](./administration/module-component-management/add-custom-module.md) +- [Add Store API Route](./framework/store-api/add-store-api-route.md) +- [Add Scheduled Task](./plugin-fundamentals/add-scheduled-task.md) -Each lifecycle page links to the corresponding implementation guide and includes troubleshooting at the boundaries where failures commonly occur. +This page provides the shared model; the implementation guides provide the feature-specific wiring and runtime checks. ## Generated code and Shopware versions From 89f76a42acd00abd2f4754f4fe9c1a593a412ffd Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 12:58:19 +0200 Subject: [PATCH 12/26] Update add-store-api-route.md --- .../plugins/plugins/framework/store-api/add-store-api-route.md | 1 - 1 file changed, 1 deletion(-) diff --git a/guides/plugins/plugins/framework/store-api/add-store-api-route.md b/guides/plugins/plugins/framework/store-api/add-store-api-route.md index 47f2f8ed28..68ed01c3b5 100644 --- a/guides/plugins/plugins/framework/store-api/add-store-api-route.md +++ b/guides/plugins/plugins/framework/store-api/add-store-api-route.md @@ -419,4 +419,3 @@ A route class on disk does not prove that the route is usable. Check the boundar 4. Make an authenticated Store API request to verify execution. A response requiring `sw-access-key` means that routing reached Store API authentication; it is not evidence that the route is missing. Treat route absence, container errors, authentication responses, and endpoint-specific errors as different failure boundaries. - From 6058b7d0a79d97fbe113eb62f8ade56cacee29e8 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 13:04:50 +0200 Subject: [PATCH 13/26] Update add-custom-module.md --- .../module-component-management/add-custom-module.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/guides/plugins/plugins/administration/module-component-management/add-custom-module.md b/guides/plugins/plugins/administration/module-component-management/add-custom-module.md index e0005616b4..8261efc346 100644 --- a/guides/plugins/plugins/administration/module-component-management/add-custom-module.md +++ b/guides/plugins/plugins/administration/module-component-management/add-custom-module.md @@ -14,8 +14,7 @@ Inside the `module` directory lies the list of several modules, each having thei ## Prerequisites -This guide **does not** explain how to create a new plugin for Shopware 6. -Head over to our Plugin base guide to learn how to create a plugin at first: +This guide **does not** explain how to create a new plugin for Shopware 6. Review our Plugin base guide to learn how to create a plugin: From e92419143b577d044af685737fbdb1b5304c2982 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 14:55:21 +0200 Subject: [PATCH 14/26] Update add-scheduled-task.md --- .../plugins/plugins/plugin-fundamentals/add-scheduled-task.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md b/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md index cf3238ecea..dbc5727a62 100644 --- a/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md +++ b/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md @@ -160,8 +160,6 @@ In order to properly test your scheduled task, you first have to run the command Now you still need to run the command `bin/console messenger:consume` to actually execute the dispatched messages. Make sure, that the `status` of your scheduled task is set to `scheduled` in the `scheduled_task` table, otherwise it won't be executed. This is not necessary, when you're using the admin worker. - - ## Verify the complete task lifecycle A scheduled task needs both the task definition and its handler. Check the boundaries separately: From bb015ed7b4c25df112d4411a169b9aeb2fc2ee32 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 14:59:26 +0200 Subject: [PATCH 15/26] docs: reframe generator wiring as troubleshooting reference --- .../add-custom-module.md | 15 +-- guides/plugins/plugins/creating-plugins.md | 16 ---- .../store-api/add-store-api-route.md | 11 --- guides/plugins/plugins/index.md | 1 - guides/plugins/plugins/plugin-base-guide.md | 2 +- .../plugin-fundamentals/add-scheduled-task.md | 12 +-- .../plugins/plugin-fundamentals/index.md | 2 - .../controllers/add-custom-controller.md | 12 --- .../plugins/storefront/controllers/index.md | 4 - .../javascript/add-custom-javascript.md | 11 --- .../plugins/storefront/javascript/index.md | 2 +- .../understanding-plugin-feature-wiring.md | 96 +++++++++++-------- 12 files changed, 61 insertions(+), 123 deletions(-) diff --git a/guides/plugins/plugins/administration/module-component-management/add-custom-module.md b/guides/plugins/plugins/administration/module-component-management/add-custom-module.md index 8261efc346..f42b23d61a 100644 --- a/guides/plugins/plugins/administration/module-component-management/add-custom-module.md +++ b/guides/plugins/plugins/administration/module-component-management/add-custom-module.md @@ -14,7 +14,8 @@ Inside the `module` directory lies the list of several modules, each having thei ## Prerequisites -This guide **does not** explain how to create a new plugin for Shopware 6. Review our Plugin base guide to learn how to create a plugin: +This guide **does not** explain how to create a new plugin for Shopware 6. +Head over to our Plugin base guide to learn how to create a plugin at first: @@ -292,18 +293,6 @@ Shopware.Module.register('swag-example', { }); ``` -## Verify module discovery - -An Administration build proves compilation, not that the module is discoverable or navigable. Check the boundaries separately: - -1. Confirm that the plugin Administration `main.js` imports the module. -2. Confirm that the module is registered with `Shopware.Module.register()`. -3. Confirm that navigation paths resolve to registered module routes and components. -4. Confirm that labels and titles use existing snippet keys. -5. Run `shopware-cli project admin-build`, refresh the Administration, and open the initial route. - -If the build succeeds but the module is missing, inspect the entry-point import, module registration, navigation path, and snippets before changing the page component. Preserve existing imports, routes, snippets, and unrelated modules when adding generated code. - ## Next steps As you might have noticed, we are just adding a custom module to the module. diff --git a/guides/plugins/plugins/creating-plugins.md b/guides/plugins/plugins/creating-plugins.md index 16984f79ba..bd7edc29d8 100644 --- a/guides/plugins/plugins/creating-plugins.md +++ b/guides/plugins/plugins/creating-plugins.md @@ -105,11 +105,6 @@ related files and service definitions; deleting only one file later can leave br references or an invalid service configuration. If you are unsure whether you need an option, use `--no-scaffold` and add the feature from its focused guide instead. -When a generated feature does not behave as expected, [Understanding Plugin Feature -Wiring](./understanding-plugin-feature-wiring.md) explains the shared boundaries to -check. The focused implementation guides contain the feature-specific verification -steps. - ::: info Generated files are tied to the Shopware version you run the command on. When your plugin supports several Shopware versions, treat the output as a starting point and verify it against the version you target. ::: @@ -198,17 +193,6 @@ At a minimum, it must define: The `extra.shopware-plugin-class` value must reference your plugin’s base PHP class (e.g. `Swag\\BasicExample\\SwagBasicExample`). -::: warning -`bin/console plugin:create` reporting success does not prove the plugin is installable. -If the plugin name and namespace do not follow `UpperCamelCase`, the generated -`extra.shopware-plugin-class` can reference a class that does not exist. The plugin is -still discovered by `bin/console plugin:refresh`, but `bin/console plugin:install` then -fails with `PluginBaseClassNotFoundException`. Check that -`extra.shopware-plugin-class` matches the PSR-4 namespace and the base class file name -before installing, and run `shopware-cli extension validate` to catch the remaining -metadata gaps. -::: - `shopware-platform-plugin` is the only Composer type Shopware treats as a plugin. A package with any other type — `library`, `project`, or the `shopware-app` type used for apps — is not picked up as a plugin and never appears in the Administration, even if everything else is set up correctly. Shopware also rejects a plugin whose `extra.shopware-plugin-class` or `extra.label` is missing. The `autoload.psr-4` namespace must match your directory structure. If you change the path (for example, not using `src/`), your folders must reflect that configuration. diff --git a/guides/plugins/plugins/framework/store-api/add-store-api-route.md b/guides/plugins/plugins/framework/store-api/add-store-api-route.md index 68ed01c3b5..447b14100f 100644 --- a/guides/plugins/plugins/framework/store-api/add-store-api-route.md +++ b/guides/plugins/plugins/framework/store-api/add-store-api-route.md @@ -408,14 +408,3 @@ export default class ExamplePlugin extends PluginBaseClass { } } ``` - -## Verify route discovery and execution - -A route class on disk does not prove that the route is usable. Check the boundaries separately: - -1. Confirm the route attribute uses the Store API scope and that the route class is registered as a service. -2. Confirm that `src/Resources/config/routes.php` imports the route. -3. Run `bin/console debug:router store-api.example.search` (or your route name) to prove discovery. -4. Make an authenticated Store API request to verify execution. - -A response requiring `sw-access-key` means that routing reached Store API authentication; it is not evidence that the route is missing. Treat route absence, container errors, authentication responses, and endpoint-specific errors as different failure boundaries. diff --git a/guides/plugins/plugins/index.md b/guides/plugins/plugins/index.md index 55217adfd1..e0c6f640a6 100644 --- a/guides/plugins/plugins/index.md +++ b/guides/plugins/plugins/index.md @@ -132,5 +132,4 @@ It is perfectly valid to ship multiple separate plugins, but keeping them in a s ## Next steps * Review the [Plugin base guide](./plugin-base-guide.md) to learn how to create plugins -* Read [Understanding Plugin Feature Wiring](./understanding-plugin-feature-wiring.md) to see how a plugin feature connects from source files to runtime * Make note of [CI](../../development/testing/ci.md) and other testing guidance to prevent upgrade-related regressions diff --git a/guides/plugins/plugins/plugin-base-guide.md b/guides/plugins/plugins/plugin-base-guide.md index dcd4f4a999..b9729d3d02 100644 --- a/guides/plugins/plugins/plugin-base-guide.md +++ b/guides/plugins/plugins/plugin-base-guide.md @@ -28,7 +28,7 @@ This guide outlines the typical development flow when creating a Shopware plugin Most steps above can be generated instead of written by hand: -* `bin/console plugin:create` scaffolds the plugin; see the [Creating Plugins guide](creating-plugins.md); [Understanding Plugin Feature Wiring](understanding-plugin-feature-wiring.md) explains how generated pieces connect +* `bin/console plugin:create` scaffolds the plugin; see the [Creating Plugins guide](creating-plugins.md) * The [Shopware 6 Toolbox plugin](../../development/tooling/shopware-toolbox.md) generates plugins, subscribers, scheduled tasks, migrations, and Administration modules from PHPStorm, using file templates you can adapt to your own conventions ## Upgrade readiness diff --git a/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md b/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md index dbc5727a62..9a5e612d5a 100644 --- a/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md +++ b/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md @@ -160,17 +160,7 @@ In order to properly test your scheduled task, you first have to run the command Now you still need to run the command `bin/console messenger:consume` to actually execute the dispatched messages. Make sure, that the `status` of your scheduled task is set to `scheduled` in the `scheduled_task` table, otherwise it won't be executed. This is not necessary, when you're using the admin worker. -## Verify the complete task lifecycle - -A scheduled task needs both the task definition and its handler. Check the boundaries separately: - -1. Confirm that the task is tagged with `shopware.scheduled.task`. -2. Confirm that the handler is registered with `messenger.message_handler` and handles the task message. -3. Run `bin/console scheduled-task:register`, then use `bin/console scheduled-task:list | grep your-task-name` to confirm the persisted task. -4. Run `bin/console scheduled-task:run` to dispatch due tasks. -5. Run `bin/console messenger:consume` (unless the Administration worker is handling the queue) and verify that the handler executes. - -A task appearing in `scheduled-task:list` proves registration and persistence, not execution. If it is registered but does not run, check its status, schedule, handler registration, runner, and message queue separately. + ## Next steps diff --git a/guides/plugins/plugins/plugin-fundamentals/index.md b/guides/plugins/plugins/plugin-fundamentals/index.md index c37879b156..a01ec59b6e 100644 --- a/guides/plugins/plugins/plugin-fundamentals/index.md +++ b/guides/plugins/plugins/plugin-fundamentals/index.md @@ -9,8 +9,6 @@ nav: Plugin fundamentals are the building blocks for adding behavior to a Shopware plugin. Use this page to jump directly to the task you want to solve. -If you are adding generated or manually created components to an existing plugin, [Understanding Plugin Feature Wiring](../understanding-plugin-feature-wiring.md) explains how source files, framework registration, discovery, build/cache, and runtime fit together. - ## What do you want to do? - Run code when a plugin is installed, updated, activated, deactivated, or uninstalled: [Plugin lifecycle](plugin-lifecycle.md) diff --git a/guides/plugins/plugins/storefront/controllers/add-custom-controller.md b/guides/plugins/plugins/storefront/controllers/add-custom-controller.md index f2f0e0b6c5..421d5d0b14 100644 --- a/guides/plugins/plugins/storefront/controllers/add-custom-controller.md +++ b/guides/plugins/plugins/storefront/controllers/add-custom-controller.md @@ -311,18 +311,6 @@ public function showExample(Request $request, SalesChannelContext $context): Res } ``` -## Verify the feature wiring - -A controller class alone does not make a URL available. Verify the boundaries in order: - -1. Confirm that the controller has the expected route attributes. -2. Confirm that `src/Resources/config/routes.php` imports the controller. -3. Confirm that the controller is registered as a public service and that its container is injected. -4. Run `bin/console debug:router | grep your-route` to prove route discovery. -5. Clear the cache with `bin/console cache:clear`, then make a real request and check the response. - -A route appearing in `debug:router` proves discovery, not that the controller can be constructed. If a plugin already has routing or service configuration, extend the existing files instead of replacing them. - ## Next steps Since you've already created a controller now, which is also part of creating a so-called "page" in Shopware, you might want to head over to our guide about [creating a page](../controllers/add-custom-page.md). diff --git a/guides/plugins/plugins/storefront/controllers/index.md b/guides/plugins/plugins/storefront/controllers/index.md index 11bca212ec..9bdd7b359d 100644 --- a/guides/plugins/plugins/storefront/controllers/index.md +++ b/guides/plugins/plugins/storefront/controllers/index.md @@ -50,7 +50,3 @@ Caching: * Use Symfony flash bags for error reporting. * Storefront functionality should be available inside the Store API too. - -## Next steps - -For implementation and troubleshooting, see [Add Custom Controller](add-custom-controller.md). diff --git a/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md b/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md index 7bf4848526..77e7942e21 100644 --- a/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md +++ b/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md @@ -266,17 +266,6 @@ composer run build:js:storefront If you now scroll to the bottom of your page an alert should appear. -## Verify registration and runtime - -An asset build proves compilation, not initialization. Check the boundaries separately: - -1. Confirm that the plugin class is imported and registered from `main.js`. -2. Confirm that the selector passed to `PluginManager.register()` is present in the rendered HTML. -3. Run the Storefront build and reload the page. -4. Check the browser console and use a temporary log in `init()` to prove runtime initialization. - -If the selector is absent, inspect the Twig template and cache. If the selector is present but `init()` does not run, inspect the entry-point import, registration, and the generated asset. - ## Next steps With your own first JavaScript plugin now running, you might want to start [listening to JavaScript events](./reacting-to-javascript-events.md) or [overriding other JavaScript plugins](./override-existing-javascript.md). diff --git a/guides/plugins/plugins/storefront/javascript/index.md b/guides/plugins/plugins/storefront/javascript/index.md index e2be04a72a..314ecb2ed7 100644 --- a/guides/plugins/plugins/storefront/javascript/index.md +++ b/guides/plugins/plugins/storefront/javascript/index.md @@ -6,7 +6,7 @@ nav: # Storefront JavaScript -This section explains how to extend and customize the Storefront using JavaScript plugins. It covers creating custom plugins, understanding how registration and DOM initialization fit together, overriding existing functionality, reacting to events, loading external scripts, and interacting with the Store API. +This section explains how to extend and customize the Storefront using JavaScript plugins. It covers creating custom plugins, overriding existing functionality, reacting to events, loading external scripts, and interacting with the Store API. * [Add Custom JavaScript](./add-custom-javascript.md) * [Add JavaScript as Script Tag](./add-javascript-as-script-tag.md) diff --git a/guides/plugins/plugins/understanding-plugin-feature-wiring.md b/guides/plugins/plugins/understanding-plugin-feature-wiring.md index ecd21c2e58..9da7e82ce5 100644 --- a/guides/plugins/plugins/understanding-plugin-feature-wiring.md +++ b/guides/plugins/plugins/understanding-plugin-feature-wiring.md @@ -1,16 +1,18 @@ --- nav: - title: Understanding Plugin Feature Wiring + title: Understanding Generated Plugin Feature Wiring position: 25 --- -# Understanding Plugin Feature Wiring +# Understanding Generated Plugin Feature Wiring -A Shopware plugin feature is usually a chain of coordinated pieces rather than one file. Understanding that chain makes generated code easier to adapt and failures much faster to localize. +Use this page when a generated plugin feature is present but does not behave as expected. It is a troubleshooting reference for generated output, not a replacement for the normal plugin scaffolding workflow. + +Generators create several coordinated artifacts. The exact files and conventions depend on the generator and the Shopware version, but the runtime path usually looks like this: ```text -feature intent +generator output ↓ source artifacts ↓ @@ -21,59 +23,79 @@ discovery build / cache ↓ runtime - ↓ -verification ``` -A feature can be correct at one boundary and broken at the next. A PHP class existing on disk does not prove that Symfony registered it. A route appearing in `debug:router` does not prove that its service can be constructed. A successful asset build does not prove that a browser can initialize the feature. +A generated file being present does not prove that the feature is registered, discovered, built, or usable at runtime. When something is broken, identify the first boundary that fails instead of changing later layers blindly. -## Scaffolding is a starting point +## What generated features usually connect -The Core `bin/console plugin:create` command can create a minimal plugin or optional example components. Those examples encode conventions for the Shopware version running the command, but they are still starting points for your implementation. +### PHP features -If you already know which feature you need, a minimal plugin plus the focused feature guide is often easier to reason about than generating every optional example. +Generated commands, subscribers, services, and similar PHP features typically combine a class with service-container configuration. The service tag is what tells Symfony or Shopware how the class participates in the framework. -Generated features also differ in size. A command or subscriber may need a class and service registration. An Administration module or Storefront JavaScript plugin coordinates several source, build, and runtime layers. +For example, `console.command`, `kernel.event_subscriber`, and `shopware.scheduled.task` describe different kinds of participation. A correct class with missing or mismatched service wiring can therefore look complete on disk while remaining invisible to the framework. -## The five boundaries to check +### Routes -1. **Code** — Is the class or component present, and does its namespace/path match autoloading? -2. **Registration** — Is the service, route, module, task, or entry point registered? -3. **Discovery** — Can Shopware prove that it found the feature? -4. **Build/cache** — Did the relevant container, Twig cache, Administration build, or Storefront build refresh? -5. **Runtime** — Can the feature actually execute, open, or initialize? +Generated Storefront and Store API routes usually involve both the route class and routing configuration. The route can exist on disk without being imported into the application's routing configuration. -Checking these boundaries in order prevents changes to working code when the failure is really registration, authentication, cache, or runtime wiring. +`debug:router` is useful here because it separates route discovery from request handling. A route that is discovered can still fail later because its service cannot be constructed, authentication rejects the request, or the endpoint itself throws an exception. -## Common framework wiring +### Scheduled tasks -### Composer and PSR-4 +A generated scheduled task has two distinct runtime roles: the scheduled-task definition and its handler. Registration of the task explains why it appears in the scheduled-task storage; handler registration and the message queue explain whether the work actually executes. -When a class is not found, compare the Composer PSR-4 prefix, PHP namespace, and filesystem path. All three must describe the same class. +A task appearing in `scheduled-task:list` therefore proves persistence, not successful execution. If a generated task is registered but does not run, the handler, task status, runner, and message consumer are separate things to investigate. -### Symfony services +### Administration modules -Many plugin features are services. Tags such as `kernel.event_subscriber`, `console.command`, and `shopware.scheduled.task` tell Symfony and Shopware how the service participates in the framework. +A generated Administration module crosses several layers: the plugin's Administration entry point, module registration, routes and components, snippets, and the Administration build. -Service configuration is a separate boundary from the class itself. A correct PHP class whose service definition references a different class name fails at registration, and Shopware may only surface that mismatch later, when the plugin is activated. +A successful build only proves that the assets compile. It does not prove that the module was imported, registered, or reachable through its navigation and routes. -### Routes +### Storefront JavaScript + +A generated Storefront JavaScript plugin also crosses several layers. The entry point imports and registers the plugin, the generated template or markup provides any selector it depends on, and the Storefront build produces the asset that the browser loads. + +A successful build therefore does not prove that the browser initialized the plugin. If the generated asset exists but `init()` never runs, look at the entry-point registration, selector, rendered markup, and cache separately. + +### Plugin configuration -Use `debug:router` to prove route discovery, then make a real request to verify the route is accessible. A missing route, container-construction error, authentication response, and endpoint exception are different failure boundaries. +Generated `config.xml` output has a similar distinction. XML validation or successful loading proves that the configuration is structurally understood; it does not by itself prove that the expected field is visible and behaves correctly in the Administration. -A plugin does not have routing configuration until something adds it. A controller class can be present and correct while its route is invisible because no `routes.php` imports it. +## The troubleshooting boundaries -### Configuration +When generated output is not working, use these boundaries in order: -A valid `config.xml` proves structural validity. The stronger user-facing check is that the field appears and behaves correctly in the Administration. +1. **Source** — Does the generated class or component exist, and do its path and namespace match the generated references? +2. **Registration** — Did the generator create the service, route, module entry point, task handler, or other registration that the feature requires? +3. **Discovery** — Can Shopware or the underlying framework show that it found the generated feature? +4. **Build / cache** — Has the relevant container, Twig cache, Administration build, or Storefront build caught up with the generated files? +5. **Runtime** — Can the feature actually execute, initialize, open, or respond? -### Builds and caches +The important distinction is between **discovery** and **runtime**. For example, a route appearing in `debug:router` proves that routing found it, but not that its service can be constructed. Likewise, a compiled JavaScript asset proves compilation, but not that a browser can initialize the generated plugin. -Build commands compile assets. Cache clears refresh runtime-discovered configuration and templates. They are related but not interchangeable; for example, a successful Storefront build does not itself guarantee that changed Twig output is rendered. +## Common generator failure patterns -## Focused implementation guides +| Symptom | Useful context | +| --- | --- | +| `plugin:refresh` finds the plugin but installation fails with a base-class error | Compare the generated plugin class, `extra.shopware-plugin-class`, and PSR-4 namespace/path. | +| A generated command or subscriber is present but never appears to run | The PHP class is only one part of the feature; inspect its service registration and tag. | +| A generated route is missing from `debug:router` | Focus on route imports and route registration before debugging the endpoint itself. | +| A scheduled task is listed but never executes | Task persistence and handler execution are separate boundaries; inspect the handler and message processing. | +| Administration assets build successfully but the generated module is missing | Compilation does not prove entry-point import, module registration, or navigation wiring. | +| Storefront assets build successfully but generated JavaScript does not initialize | Check registration, the selector/markup used by the plugin, the generated asset, and cache. | +| Generated configuration loads but a field is not visible as expected | Structural validity and Administration rendering are different checks. | -Use the feature-specific guides for implementation details and verification steps: +## Generated output is version-specific + +Generators encode conventions from the Shopware version they run against. File locations, entry points, registration patterns, and generated examples can change between versions. + +When generated output looks different from an older guide, prefer the output from the generator you are actually using and use the focused documentation to understand the relevant concept. Do not assume that a generated example from another Shopware version is interchangeable. + +## When you need the implementation details + +Use the focused feature guides when you intentionally need to understand or modify the generated implementation: - [Add Custom Controller](./storefront/controllers/add-custom-controller.md) - [Add Custom JavaScript](./storefront/javascript/add-custom-javascript.md) @@ -81,10 +103,4 @@ Use the feature-specific guides for implementation details and verification step - [Add Store API Route](./framework/store-api/add-store-api-route.md) - [Add Scheduled Task](./plugin-fundamentals/add-scheduled-task.md) -This page provides the shared model; the implementation guides provide the feature-specific wiring and runtime checks. - -## Generated code and Shopware versions - -Generated files are tied to the Shopware version whose generator produced them. When a framework convention matters, compare generated output with the corresponding Core scaffolding generator and stubs for the version you target. - -The newer `shopware-cli extension create ...` generator work is developed separately from Core `plugin:create`. Verify CLI generator availability and behavior against `shopware/shopware-cli` rather than assuming the two generator systems are equivalent. +Those guides explain the underlying feature conventions. This page is the shorter map for understanding why generated pieces have to work together and where to look when they do not. From e51fa3188681ef4f77e656b6cea9de95e3252c25 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 15:00:04 +0200 Subject: [PATCH 16/26] docs: clarify generated output is troubleshooting context --- guides/plugins/plugins/creating-plugins.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/guides/plugins/plugins/creating-plugins.md b/guides/plugins/plugins/creating-plugins.md index bd7edc29d8..72ca3f455b 100644 --- a/guides/plugins/plugins/creating-plugins.md +++ b/guides/plugins/plugins/creating-plugins.md @@ -102,11 +102,13 @@ components use paths such as: Treat generated examples as starting points. Selecting an option can create several related files and service definitions; deleting only one file later can leave broken -references or an invalid service configuration. If you are unsure whether you need -an option, use `--no-scaffold` and add the feature from its focused guide instead. +references or an invalid service configuration. If you are unsure whether you need an +option, use `--no-scaffold` and add the feature from its focused guide instead. + +If generated output does not behave as expected, use [Understanding Generated Plugin Feature Wiring](./understanding-plugin-feature-wiring.md) as a troubleshooting reference. It explains how generated artifacts connect to registration, discovery, builds, and runtime without replacing the normal scaffolding workflow. ::: info -Generated files are tied to the Shopware version you run the command on. When your plugin supports several Shopware versions, treat the output as a starting point and verify it against the version you target. +Generated files are tied to the Shopware version you run the command on. When your plugin supports several Shopware versions, treat the output as an example for that version and compare it with the focused guide when adapting it. ::: Make sure to adjust the namespace in the generated files as per your needs. From 0a632e34890c727d35213e9a47576ec7d81ca246 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 15:00:35 +0200 Subject: [PATCH 17/26] docs: keep generated wiring reference out of primary navigation --- .../plugins/understanding-plugin-feature-wiring.md | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/guides/plugins/plugins/understanding-plugin-feature-wiring.md b/guides/plugins/plugins/understanding-plugin-feature-wiring.md index 9da7e82ce5..aebfc55c4c 100644 --- a/guides/plugins/plugins/understanding-plugin-feature-wiring.md +++ b/guides/plugins/plugins/understanding-plugin-feature-wiring.md @@ -1,10 +1,3 @@ ---- -nav: - title: Understanding Generated Plugin Feature Wiring - position: 25 - ---- - # Understanding Generated Plugin Feature Wiring Use this page when a generated plugin feature is present but does not behave as expected. It is a troubleshooting reference for generated output, not a replacement for the normal plugin scaffolding workflow. @@ -93,9 +86,9 @@ Generators encode conventions from the Shopware version they run against. File l When generated output looks different from an older guide, prefer the output from the generator you are actually using and use the focused documentation to understand the relevant concept. Do not assume that a generated example from another Shopware version is interchangeable. -## When you need the implementation details +## Where to read more -Use the focused feature guides when you intentionally need to understand or modify the generated implementation: +The focused feature guides explain the underlying concepts represented by generated output. Use them when the generated result needs closer inspection or deliberate modification: - [Add Custom Controller](./storefront/controllers/add-custom-controller.md) - [Add Custom JavaScript](./storefront/javascript/add-custom-javascript.md) @@ -103,4 +96,4 @@ Use the focused feature guides when you intentionally need to understand or modi - [Add Store API Route](./framework/store-api/add-store-api-route.md) - [Add Scheduled Task](./plugin-fundamentals/add-scheduled-task.md) -Those guides explain the underlying feature conventions. This page is the shorter map for understanding why generated pieces have to work together and where to look when they do not. +These are implementation references; the normal scaffolding workflow remains the preferred way to create the feature. From de1e006fe108118dab3b1f0cc29b6be437c07595 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 15:13:31 +0200 Subject: [PATCH 18/26] docs: fold generator context into existing plugin guides --- guides/plugins/plugins/plugin-base-guide.md | 2 + .../understanding-plugin-feature-wiring.md | 99 ------------------- 2 files changed, 2 insertions(+), 99 deletions(-) delete mode 100644 guides/plugins/plugins/understanding-plugin-feature-wiring.md diff --git a/guides/plugins/plugins/plugin-base-guide.md b/guides/plugins/plugins/plugin-base-guide.md index b9729d3d02..7be6cb2e7b 100644 --- a/guides/plugins/plugins/plugin-base-guide.md +++ b/guides/plugins/plugins/plugin-base-guide.md @@ -31,6 +31,8 @@ Most steps above can be generated instead of written by hand: * `bin/console plugin:create` scaffolds the plugin; see the [Creating Plugins guide](creating-plugins.md) * The [Shopware 6 Toolbox plugin](../../development/tooling/shopware-toolbox.md) generates plugins, subscribers, scheduled tasks, migrations, and Administration modules from PHPStorm, using file templates you can adapt to your own conventions +When a generator creates an optional feature, the files it produces are usually parts of one piece of framework wiring. For example, a generated controller can depend on both route configuration and service registration, while a scheduled task has separate task and handler roles. The focused guides explain those pieces in context; you do not normally need to reproduce the wiring manually. + ## Upgrade readiness Design plugins so that: diff --git a/guides/plugins/plugins/understanding-plugin-feature-wiring.md b/guides/plugins/plugins/understanding-plugin-feature-wiring.md deleted file mode 100644 index aebfc55c4c..0000000000 --- a/guides/plugins/plugins/understanding-plugin-feature-wiring.md +++ /dev/null @@ -1,99 +0,0 @@ -# Understanding Generated Plugin Feature Wiring - -Use this page when a generated plugin feature is present but does not behave as expected. It is a troubleshooting reference for generated output, not a replacement for the normal plugin scaffolding workflow. - -Generators create several coordinated artifacts. The exact files and conventions depend on the generator and the Shopware version, but the runtime path usually looks like this: - -```text -generator output - ↓ -source artifacts - ↓ -framework registration - ↓ -discovery - ↓ -build / cache - ↓ -runtime -``` - -A generated file being present does not prove that the feature is registered, discovered, built, or usable at runtime. When something is broken, identify the first boundary that fails instead of changing later layers blindly. - -## What generated features usually connect - -### PHP features - -Generated commands, subscribers, services, and similar PHP features typically combine a class with service-container configuration. The service tag is what tells Symfony or Shopware how the class participates in the framework. - -For example, `console.command`, `kernel.event_subscriber`, and `shopware.scheduled.task` describe different kinds of participation. A correct class with missing or mismatched service wiring can therefore look complete on disk while remaining invisible to the framework. - -### Routes - -Generated Storefront and Store API routes usually involve both the route class and routing configuration. The route can exist on disk without being imported into the application's routing configuration. - -`debug:router` is useful here because it separates route discovery from request handling. A route that is discovered can still fail later because its service cannot be constructed, authentication rejects the request, or the endpoint itself throws an exception. - -### Scheduled tasks - -A generated scheduled task has two distinct runtime roles: the scheduled-task definition and its handler. Registration of the task explains why it appears in the scheduled-task storage; handler registration and the message queue explain whether the work actually executes. - -A task appearing in `scheduled-task:list` therefore proves persistence, not successful execution. If a generated task is registered but does not run, the handler, task status, runner, and message consumer are separate things to investigate. - -### Administration modules - -A generated Administration module crosses several layers: the plugin's Administration entry point, module registration, routes and components, snippets, and the Administration build. - -A successful build only proves that the assets compile. It does not prove that the module was imported, registered, or reachable through its navigation and routes. - -### Storefront JavaScript - -A generated Storefront JavaScript plugin also crosses several layers. The entry point imports and registers the plugin, the generated template or markup provides any selector it depends on, and the Storefront build produces the asset that the browser loads. - -A successful build therefore does not prove that the browser initialized the plugin. If the generated asset exists but `init()` never runs, look at the entry-point registration, selector, rendered markup, and cache separately. - -### Plugin configuration - -Generated `config.xml` output has a similar distinction. XML validation or successful loading proves that the configuration is structurally understood; it does not by itself prove that the expected field is visible and behaves correctly in the Administration. - -## The troubleshooting boundaries - -When generated output is not working, use these boundaries in order: - -1. **Source** — Does the generated class or component exist, and do its path and namespace match the generated references? -2. **Registration** — Did the generator create the service, route, module entry point, task handler, or other registration that the feature requires? -3. **Discovery** — Can Shopware or the underlying framework show that it found the generated feature? -4. **Build / cache** — Has the relevant container, Twig cache, Administration build, or Storefront build caught up with the generated files? -5. **Runtime** — Can the feature actually execute, initialize, open, or respond? - -The important distinction is between **discovery** and **runtime**. For example, a route appearing in `debug:router` proves that routing found it, but not that its service can be constructed. Likewise, a compiled JavaScript asset proves compilation, but not that a browser can initialize the generated plugin. - -## Common generator failure patterns - -| Symptom | Useful context | -| --- | --- | -| `plugin:refresh` finds the plugin but installation fails with a base-class error | Compare the generated plugin class, `extra.shopware-plugin-class`, and PSR-4 namespace/path. | -| A generated command or subscriber is present but never appears to run | The PHP class is only one part of the feature; inspect its service registration and tag. | -| A generated route is missing from `debug:router` | Focus on route imports and route registration before debugging the endpoint itself. | -| A scheduled task is listed but never executes | Task persistence and handler execution are separate boundaries; inspect the handler and message processing. | -| Administration assets build successfully but the generated module is missing | Compilation does not prove entry-point import, module registration, or navigation wiring. | -| Storefront assets build successfully but generated JavaScript does not initialize | Check registration, the selector/markup used by the plugin, the generated asset, and cache. | -| Generated configuration loads but a field is not visible as expected | Structural validity and Administration rendering are different checks. | - -## Generated output is version-specific - -Generators encode conventions from the Shopware version they run against. File locations, entry points, registration patterns, and generated examples can change between versions. - -When generated output looks different from an older guide, prefer the output from the generator you are actually using and use the focused documentation to understand the relevant concept. Do not assume that a generated example from another Shopware version is interchangeable. - -## Where to read more - -The focused feature guides explain the underlying concepts represented by generated output. Use them when the generated result needs closer inspection or deliberate modification: - -- [Add Custom Controller](./storefront/controllers/add-custom-controller.md) -- [Add Custom JavaScript](./storefront/javascript/add-custom-javascript.md) -- [Add Custom Module](./administration/module-component-management/add-custom-module.md) -- [Add Store API Route](./framework/store-api/add-store-api-route.md) -- [Add Scheduled Task](./plugin-fundamentals/add-scheduled-task.md) - -These are implementation references; the normal scaffolding workflow remains the preferred way to create the feature. From 02815ee10b64f4d823f1e62c5bfac7bcae3ec12e Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 15:14:02 +0200 Subject: [PATCH 19/26] docs: keep generator context on existing pages --- guides/plugins/plugins/creating-plugins.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guides/plugins/plugins/creating-plugins.md b/guides/plugins/plugins/creating-plugins.md index 72ca3f455b..90092d6875 100644 --- a/guides/plugins/plugins/creating-plugins.md +++ b/guides/plugins/plugins/creating-plugins.md @@ -43,7 +43,7 @@ can generate an event subscriber, scheduled task, migration, Administration modu or other component directly inside an existing plugin. AI coding tools can also help you create or adapt these components, but always verify their namespaces, generated paths, service registration, and Shopware-version compatibility against the linked -guides. + guides. ::: The command asks for a plugin name and namespace (both UpperCamelCase) if you do not pass them as arguments, then asks whether it should scaffold optional files. It always generates the files an extension needs to be installable: `composer.json`, the plugin base class, `config.xml`, `.gitignore`, and the PHPUnit setup. @@ -105,7 +105,7 @@ related files and service definitions; deleting only one file later can leave br references or an invalid service configuration. If you are unsure whether you need an option, use `--no-scaffold` and add the feature from its focused guide instead. -If generated output does not behave as expected, use [Understanding Generated Plugin Feature Wiring](./understanding-plugin-feature-wiring.md) as a troubleshooting reference. It explains how generated artifacts connect to registration, discovery, builds, and runtime without replacing the normal scaffolding workflow. +If generated output does not behave as expected, use the existing feature guide to understand the generated pieces in context. The generator connects source files with registration, discovery, and build/runtime wiring; a generated file being present does not by itself mean that Shopware can use it. ::: info Generated files are tied to the Shopware version you run the command on. When your plugin supports several Shopware versions, treat the output as an example for that version and compare it with the focused guide when adapting it. From ffcf185d7929a3b0dfb1bdf00b5192e1fddef7e6 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 15:14:42 +0200 Subject: [PATCH 20/26] docs: fix generator guide wording --- guides/plugins/plugins/creating-plugins.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/plugins/plugins/creating-plugins.md b/guides/plugins/plugins/creating-plugins.md index 90092d6875..7e37d2a4b9 100644 --- a/guides/plugins/plugins/creating-plugins.md +++ b/guides/plugins/plugins/creating-plugins.md @@ -43,7 +43,7 @@ can generate an event subscriber, scheduled task, migration, Administration modu or other component directly inside an existing plugin. AI coding tools can also help you create or adapt these components, but always verify their namespaces, generated paths, service registration, and Shopware-version compatibility against the linked - guides. +guides. ::: The command asks for a plugin name and namespace (both UpperCamelCase) if you do not pass them as arguments, then asks whether it should scaffold optional files. It always generates the files an extension needs to be installable: `composer.json`, the plugin base class, `config.xml`, `.gitignore`, and the PHPUnit setup. From 9c59820c5c38abfcc18e6da558dbde5e97e1a292 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 15:39:23 +0200 Subject: [PATCH 21/26] docs: fold generator context into feature guides --- .../module-component-management/add-custom-module.md | 8 ++++++++ .../plugins/framework/store-api/add-store-api-route.md | 10 ++++++++++ .../plugins/plugin-fundamentals/add-scheduled-task.md | 6 ++++++ .../storefront/controllers/add-custom-controller.md | 8 ++++++++ .../storefront/javascript/add-custom-javascript.md | 8 +++++--- 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/guides/plugins/plugins/administration/module-component-management/add-custom-module.md b/guides/plugins/plugins/administration/module-component-management/add-custom-module.md index f42b23d61a..3f204913cd 100644 --- a/guides/plugins/plugins/administration/module-component-management/add-custom-module.md +++ b/guides/plugins/plugins/administration/module-component-management/add-custom-module.md @@ -40,6 +40,10 @@ import './module/swag-example'; Now your module's `index.js` will be executed. +::: info +For generated Administration modules, `main.js`, module registration, routes/components, snippets, and the Administration build are parts of the same feature. The generated entry point connects the module to the build; `Shopware.Module.register()` describes the module; its routes and components provide the UI; and snippets provide the text. This guide shows the pieces separately so their relationship is clear. +::: + ## Registering the module Your `index.js` is still empty now, so let's get going to actually create a new module. @@ -179,6 +183,10 @@ As mentioned above, Shopware 6 is looking for a `main.js` file in your plugin. Its contents get minified into a new file named after your plugin and will be moved to the `public` directory of Shopware 6 root directory. Given this plugin would be named "AdministrationNewModule", the bundled and minified javascript code for this example would be located under `/src/Resources/public/administration/js/administration-new-module.js`, once you run the command following command in your shopware root directory: +::: info +The Administration build is the build boundary, not the whole feature lifecycle. A successful build means the generated assets compiled; the module still depends on its entry-point import, registration, routes/components, and snippets being connected correctly at runtime. +::: + diff --git a/guides/plugins/plugins/framework/store-api/add-store-api-route.md b/guides/plugins/plugins/framework/store-api/add-store-api-route.md index 447b14100f..cf9567fb6e 100644 --- a/guides/plugins/plugins/framework/store-api/add-store-api-route.md +++ b/guides/plugins/plugins/framework/store-api/add-store-api-route.md @@ -50,6 +50,10 @@ abstract class AbstractExampleRoute Now we can create a new class `ExampleRoute` which uses our previously created `AbstractExampleRoute`. +::: info +A generated Store API route is normally more than the route class itself. The class defines the endpoint and response, the service definition makes the route constructible, and `routes.php` imports it for discovery. These pieces form one feature; the generator creates them together so you do not normally need to assemble the wiring by hand. +::: + ```php // /src/Core/Content/Example/SalesChannel/ExampleRoute.php /src/Resources/config/` location. Take a look at the official [Symfony documentation](https://symfony.com/doc/current/routing.html) about routes and how they are registered. +The route import is the discovery boundary: a route class can be present and correctly registered as a service without becoming a Store API endpoint until Shopware imports its route attributes. + ```php // /src/Resources/config/routes.php /src/Resources/Schema/StoreApi/` so the shopware internal OpenApi3Generator can find it (for Admin API endpoints, use `AdminApi`). diff --git a/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md b/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md index 9a5e612d5a..971d2b6222 100644 --- a/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md +++ b/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md @@ -23,6 +23,10 @@ A `ScheduledTask` and its respective `ScheduledTaskHandler` are registered in a Here's an example `services.php` containing a new `ScheduledTask` as well as a new `ScheduledTaskHandler`: +::: info +A scheduled task has two related runtime roles. The task definition describes when work is due, while the handler processes the message that Shopware dispatches. Generators create both sides and their service wiring together; seeing one generated class on disk does not mean the whole feature has been connected. +::: + ```php // /src/Resources/config/services.php /src/Resources/config/` location. Take a look at the official [Symfony documentation](https://symfony.com/doc/current/routing.html) about routes and how they are registered. +The route import is the discovery half of the feature: it tells Shopware which controller files to inspect for route attributes. A controller can therefore exist and be a valid service while still not expose a URL until its route is imported. + ::: code-group ```php [PLUGIN_ROOT/src/Resources/config/routes.php] diff --git a/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md b/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md index 77e7942e21..2a0b3d9945 100644 --- a/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md +++ b/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md @@ -68,6 +68,8 @@ Next you have to tell Shopware that your plugin should be loaded and executed. T Shopware is automatically looking for a `main.js` file in a directory `/src/Resources/app/storefront/src`, which then will be loaded automatically. Consider this to be your main storefront JavaScript entrypoint. +The entry point, plugin class, and optional DOM selector form one runtime chain. A generator creates these pieces together: `main.js` imports and registers the class, the selector connects it to rendered markup when one is used, and the Storefront build produces the asset the browser loads. Keeping that relationship in mind makes generated output easier to adapt without treating each file as an independent feature. + Create a `main.js` file inside your `/src/Resources/app/storefront/src` folder and get the PluginManager from the global window object. Then register your own plugin: ```javascript @@ -75,7 +77,7 @@ Create a `main.js` file inside your `/src/Resources/app/storefront/ // Import all necessary Storefront plugins import ExamplePlugin from './example-plugin/example-plugin.plugin'; -// Register your plugin via the existing PluginManager +// Register your custom Storefront plugin const PluginManager = window.PluginManager; PluginManager.register('ExamplePlugin', ExamplePlugin); ``` @@ -91,7 +93,7 @@ You can also bind your plugin to a DOM element by providing a css selector: // Import all necessary Storefront plugins import ExamplePlugin from './example-plugin/example-plugin.plugin'; -// Register your plugin via the existing PluginManager +// Register your custom Storefront plugin const PluginManager = window.PluginManager; PluginManager.register('ExamplePlugin', ExamplePlugin, '[data-example-plugin]'); ``` @@ -106,7 +108,7 @@ The import path can remain the same as the synchronous import. ```javascript // /src/Resources/app/storefront/src/main.js -// Register your plugin via the existing PluginManager using a dynamic import +// Register your own Storefront plugin using a dynamic import const PluginManager = window.PluginManager; PluginManager.register('ExamplePlugin', () => import('./example-plugin/example-plugin.plugin'), '[data-example-plugin]'); ``` From 58187caa125d5843f0ef5613b0f10abb720f71a9 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 16:15:46 +0200 Subject: [PATCH 22/26] Update add-store-api-route.md --- .../plugins/plugins/framework/store-api/add-store-api-route.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/plugins/plugins/framework/store-api/add-store-api-route.md b/guides/plugins/plugins/framework/store-api/add-store-api-route.md index cf9567fb6e..1e06c3d480 100644 --- a/guides/plugins/plugins/framework/store-api/add-store-api-route.md +++ b/guides/plugins/plugins/framework/store-api/add-store-api-route.md @@ -51,7 +51,7 @@ abstract class AbstractExampleRoute Now we can create a new class `ExampleRoute` which uses our previously created `AbstractExampleRoute`. ::: info -A generated Store API route is normally more than the route class itself. The class defines the endpoint and response, the service definition makes the route constructible, and `routes.php` imports it for discovery. These pieces form one feature; the generator creates them together so you do not normally need to assemble the wiring by hand. +A generated Store API route is normally more than the route class itself. The class defines the endpoint and response, the service definition registers it with the dependency injection container, and `routes.php` imports it for discovery. These pieces form one feature; the generator creates them together so you do not normally need to assemble the wiring by hand. ::: ```php From 1dffc277d2c15975a11819cb4333836dc338a27f Mon Sep 17 00:00:00 2001 From: somethings Date: Tue, 25 Aug 2026 12:43:34 +0200 Subject: [PATCH 23/26] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../plugins/storefront/controllers/add-custom-controller.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/plugins/plugins/storefront/controllers/add-custom-controller.md b/guides/plugins/plugins/storefront/controllers/add-custom-controller.md index a79919c101..338b19be02 100644 --- a/guides/plugins/plugins/storefront/controllers/add-custom-controller.md +++ b/guides/plugins/plugins/storefront/controllers/add-custom-controller.md @@ -173,7 +173,7 @@ Once we've registered our new controller, we have to tell Shopware how we want i This is done with a `routes.php` file at `/src/Resources/config/` location. Take a look at the official [Symfony documentation](https://symfony.com/doc/current/routing.html) about routes and how they are registered. -The route import is the discovery half of the feature: it tells Shopware which controller files to inspect for route attributes. A controller can therefore exist and be a valid service while still not expose a URL until its route is imported. +The route import is the discovery half of the feature: it tells Shopware which controller files to inspect for route attributes. A controller can therefore exist and be a valid service while still not exposing a URL until its route is imported. ::: code-group From d9a190a11eab558b77571bb28eb6f78ad2364a96 Mon Sep 17 00:00:00 2001 From: somethings Date: Tue, 25 Aug 2026 12:43:58 +0200 Subject: [PATCH 24/26] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../plugins/storefront/javascript/add-custom-javascript.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md b/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md index 2a0b3d9945..8a2f7d9d05 100644 --- a/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md +++ b/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md @@ -66,7 +66,7 @@ Well, and then we check if this sum is bigger or equal the total size of your we Next you have to tell Shopware that your plugin should be loaded and executed. Therefore you have to register your plugin in the PluginManager. -Shopware is automatically looking for a `main.js` file in a directory `/src/Resources/app/storefront/src`, which then will be loaded automatically. Consider this to be your main storefront JavaScript entrypoint. +Shopware is automatically looking for a `main.js` file in a directory `/src/Resources/app/storefront/src`, which then will be loaded automatically. Consider this to be your main storefront JavaScript entry point. The entry point, plugin class, and optional DOM selector form one runtime chain. A generator creates these pieces together: `main.js` imports and registers the class, the selector connects it to rendered markup when one is used, and the Storefront build produces the asset the browser loads. Keeping that relationship in mind makes generated output easier to adapt without treating each file as an independent feature. From 1332a0de3c2e5ccac3938db78e8eed74694c0963 Mon Sep 17 00:00:00 2001 From: Micha Hobert Date: Wed, 26 Aug 2026 09:01:16 +0200 Subject: [PATCH 25/26] fix/grammar-tables-and-order --- .../add-custom-module.md | 10 ++--- guides/plugins/plugins/creating-plugins.md | 40 +++++++++---------- .../store-api/add-store-api-route.md | 2 +- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/guides/plugins/plugins/administration/module-component-management/add-custom-module.md b/guides/plugins/plugins/administration/module-component-management/add-custom-module.md index 3f204913cd..6c47f402f1 100644 --- a/guides/plugins/plugins/administration/module-component-management/add-custom-module.md +++ b/guides/plugins/plugins/administration/module-component-management/add-custom-module.md @@ -181,11 +181,7 @@ This should be your snippet file now: As mentioned above, Shopware 6 is looking for a `main.js` file in your plugin. Its contents get minified into a new file named after your plugin and will be moved to the `public` directory of Shopware 6 root directory. -Given this plugin would be named "AdministrationNewModule", the bundled and minified javascript code for this example would be located under `/src/Resources/public/administration/js/administration-new-module.js`, once you run the command following command in your shopware root directory: - -::: info -The Administration build is the build boundary, not the whole feature lifecycle. A successful build means the generated assets compiled; the module still depends on its entry-point import, registration, routes/components, and snippets being connected correctly at runtime. -::: +Given this plugin would be named "AdministrationNewModule", the bundled and minified JavaScript code for this example would be located under `/src/Resources/public/administration/js/administration-new-module.js`, once you run the following command in your shopware root directory: @@ -209,6 +205,10 @@ composer run build:js:admin Your plugin has to be activated for this to work. ::: +::: info +The Administration build is the build boundary, not the whole feature lifecycle. A successful build means the generated assets are compiled; the module still depends on its entry-point import, registration, routes/components, and snippets being connected correctly at runtime. +::: + Make sure to also include that file when publishing your plugin! A copy of this file will then be put into the directory `/public/bundles/administration/administrationnewmodule/administration/js/administration-new-module.js`. diff --git a/guides/plugins/plugins/creating-plugins.md b/guides/plugins/plugins/creating-plugins.md index 7e37d2a4b9..b68ea59e1e 100644 --- a/guides/plugins/plugins/creating-plugins.md +++ b/guides/plugins/plugins/creating-plugins.md @@ -73,32 +73,32 @@ This is the recommended starting point when you already know what your plugin ne To generate a specific example instead of all of them, pass its option. Each option can also be answered interactively: -| Option | Generates | -| --- | --- | -| `--create-storefront-controller` | Example Storefront controller, its template, and a `routes.php` entry | -| `--create-store-api-route` | Example Store API route with abstract class and response class | -| `--create-event-subscriber` | Example event subscriber | -| `--create-command` | Example console command | -| `--create-scheduled-task` | Example scheduled task | -| `--create-admin-module` | Example Administration module with snippets | -| `--create-javascript-plugin` | Example Storefront JavaScript plugin | -| `--create-custom-fieldset` | Example custom fieldset (`custom-fields.xml`) | -| `--entities=Example,Foo` | Entity definition, entity, collection, and migration per entity (UpperCamelCase, comma-separated) | +| Option | Generates | +|----------------------------------|---------------------------------------------------------------------------------------------------| +| `--create-storefront-controller` | Example Storefront controller, its template, and a `routes.php` entry | +| `--create-store-api-route` | Example Store API route with abstract class and response class | +| `--create-event-subscriber` | Example event subscriber | +| `--create-command` | Example console command | +| `--create-scheduled-task` | Example scheduled task | +| `--create-admin-module` | Example Administration module with snippets | +| `--create-javascript-plugin` | Example Storefront JavaScript plugin | +| `--create-custom-fieldset` | Example custom fieldset (`custom-fields.xml`) | +| `--entities=Example,Foo` | Entity definition, entity, collection, and migration per entity (UpperCamelCase, comma-separated) | Every generator that needs a service definition also appends it to the plugin's service configuration in `src/Resources/config`. The generated files are placed below the plugin root. For example, the optional components use paths such as: -| Component | Typical generated location | -| --- | --- | -| Console command | `src/Command/` and `src/Resources/config/services.php` | -| Scheduled task | `src/ScheduledTask/` and `src/Resources/config/services.php` | -| Event subscriber | `src/Subscriber/` and `src/Resources/config/services.php` | -| Storefront controller | `src/Storefront/Controller/`, `src/Resources/views/`, and `src/Resources/config/routes.php` | -| Administration module | `src/Resources/app/administration/` | -| Storefront JavaScript plugin | `src/Resources/app/storefront/src/` | -| Custom field set | `src/Resources/config/custom-fields.xml` | +| Component | Typical generated location | +|------------------------------|---------------------------------------------------------------------------------------------| +| Console command | `src/Command/` and `src/Resources/config/services.php` | +| Scheduled task | `src/ScheduledTask/` and `src/Resources/config/services.php` | +| Event subscriber | `src/Subscriber/` and `src/Resources/config/services.php` | +| Storefront controller | `src/Storefront/Controller/`, `src/Resources/views/`, and `src/Resources/config/routes.php` | +| Administration module | `src/Resources/app/administration/` | +| Storefront JavaScript plugin | `src/Resources/app/storefront/src/` | +| Custom field set | `src/Resources/config/custom-fields.xml` | Treat generated examples as starting points. Selecting an option can create several related files and service definitions; deleting only one file later can leave broken diff --git a/guides/plugins/plugins/framework/store-api/add-store-api-route.md b/guides/plugins/plugins/framework/store-api/add-store-api-route.md index 1e06c3d480..94273eed65 100644 --- a/guides/plugins/plugins/framework/store-api/add-store-api-route.md +++ b/guides/plugins/plugins/framework/store-api/add-store-api-route.md @@ -115,7 +115,7 @@ return static function (ContainerConfigurator $configurator): void { }; ``` -The service definition is what connects the route class to the dependency injection container. If generated code already added this definition, treat it as part of the route rather than as unrelated boilerplate. +The service definition is what connects the route class to the dependency injection container. If generated code already added this definition, treat it as part of the route rather than as an unrelated boilerplate. ### Route response From f341d65257dd730ba7b920eff7164c369a907751 Mon Sep 17 00:00:00 2001 From: Micha Hobert Date: Wed, 26 Aug 2026 09:04:26 +0200 Subject: [PATCH 26/26] adjust/sentence-for-clarity --- .../plugins/plugins/plugin-fundamentals/add-scheduled-task.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md b/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md index 971d2b6222..a0e29ebe2d 100644 --- a/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md +++ b/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md @@ -24,7 +24,7 @@ A `ScheduledTask` and its respective `ScheduledTaskHandler` are registered in a Here's an example `services.php` containing a new `ScheduledTask` as well as a new `ScheduledTaskHandler`: ::: info -A scheduled task has two related runtime roles. The task definition describes when work is due, while the handler processes the message that Shopware dispatches. Generators create both sides and their service wiring together; seeing one generated class on disk does not mean the whole feature has been connected. +A scheduled task has two related runtime roles. The task definition describes when work is due, while the handler processes the message that Shopware dispatches. Generators create both sides and their service wiring together; seeing the generated PHP class in the filesystem does not mean the whole feature has been connected. ::: ```php