From 576c38d6a7bee2326728db942a4d3dbd1f6627aa Mon Sep 17 00:00:00 2001 From: somethings Date: Fri, 21 Aug 2026 15:38:54 +0200 Subject: [PATCH 01/22] 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 000000000..95b275bdb --- /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 1877b28e1cca927a767e5ee83ec84526578b91ac Mon Sep 17 00:00:00 2001 From: somethings Date: Fri, 21 Aug 2026 15:41:29 +0200 Subject: [PATCH 02/22] 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 95b275bdb..9b37f2af8 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 e55fa9895800ae545f0347806c3b750eab50b4b7 Mon Sep 17 00:00:00 2001 From: somethings Date: Fri, 21 Aug 2026 17:27:02 +0200 Subject: [PATCH 03/22] 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 000000000..bb4fbb05d --- /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 15f1bdce10918157e25883e73db3b0e4532b797b Mon Sep 17 00:00:00 2001 From: somethings Date: Fri, 21 Aug 2026 17:36:07 +0200 Subject: [PATCH 04/22] 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 000000000..7703af9d2 --- /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 000000000..4f422f082 --- /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 000000000..c1e380f21 --- /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 bb4fbb05d..000000000 --- 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 000000000..84909900c --- /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 67485004a031b309cfbde29fbc9f79cf3e0a5ad7 Mon Sep 17 00:00:00 2001 From: somethings Date: Fri, 21 Aug 2026 17:47:49 +0200 Subject: [PATCH 05/22] 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 000000000..b3389d0c0 --- /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 7703af9d2..000000000 --- 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 000000000..cd3821afa --- /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 4f422f082..000000000 --- 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 000000000..027870a0e --- /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 c1e380f21..000000000 --- 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 9b37f2af8..000000000 --- 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 314ecb2ed..ec37590b0 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 000000000..219e2db03 --- /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 84909900c..000000000 --- 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 000000000..064a8f97d --- /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 bd05cf7697b5f9a737859caeb95fa220578bab8f Mon Sep 17 00:00:00 2001 From: somethings Date: Fri, 21 Aug 2026 17:54:30 +0200 Subject: [PATCH 06/22] 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 a708d2ce0..ed61600e9 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 b3389d0c0..064bff419 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 7e7aae99c..caa4bc537 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 cd3821afa..00f7056a2 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 a01ec59b6..9846384a2 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 027870a0e..0b7f780f1 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 ec37590b0..10f57a6b1 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 219e2db03..ed684577a 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 064a8f97d..6e715e2f8 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 1cc218f64395439463dd11191e81ad454d9ac3df Mon Sep 17 00:00:00 2001 From: LApple Date: Fri, 21 Aug 2026 18:19:05 +0200 Subject: [PATCH 07/22] 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 064bff419..3e684ed57 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 2229f6bd7..0a1a028ca 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 bd7edc29d..27ff28f0d 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 00f7056a2..b95f3c13d 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 e0c6f640a..55217adfd 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 b9729d3d0..dcd4f4a99 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 0b7f780f1..13f41fbac 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 9bdd7b359..cb6096748 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 000000000..ec86d77ce --- /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 4daef7317..d00ea60f5 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 ed684577a..debaba145 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 6e715e2f8..a8fed0c2a 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 f0d652849945e58247193b007a401da8f14a700f 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/22] 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 a8fed0c2a..2676cc905 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 0020d42c3cfdd5f05e515796d5725f51ead1f149 Mon Sep 17 00:00:00 2001 From: somethings Date: Fri, 21 Aug 2026 18:34:54 +0200 Subject: [PATCH 09/22] 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 ec86d77ce..8ddfb7ddb 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 9f61fb6bf0db5a282eae9d46edd7487f08a77ac4 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 12:11:17 +0200 Subject: [PATCH 10/22] 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 8ddfb7ddb..be891d002 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 a3f52f036edef48b5613dd6152d66b450e677e15 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 12:42:55 +0200 Subject: [PATCH 11/22] 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 ed61600e9..a708d2ce0 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 f42b23d61..e0005616b 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 3e684ed57..000000000 --- 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 0a1a028ca..2229f6bd7 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 27ff28f0d..16984f79b 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 447b14100..47f2f8ed2 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 caa4bc537..7e7aae99c 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 b95f3c13d..000000000 --- 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 9a5e612d5..cf3238ece 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 9846384a2..c37879b15 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 13f41fbac..000000000 --- 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 421d5d0b1..f2f0e0b6c 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 cb6096748..11bca212e 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 be891d002..000000000 --- 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 d00ea60f5..4daef7317 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 77e7942e2..7bf484852 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 10f57a6b1..e2be04a72 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 debaba145..000000000 --- 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 2676cc905..ecd21c2e5 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 d254c0450afab815797a8ceeba63e1fe44f641b6 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 12:58:19 +0200 Subject: [PATCH 12/22] 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 47f2f8ed2..68ed01c3b 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 09f9a10f5eb92e066bc4396d41b2880798dce3a1 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 13:04:50 +0200 Subject: [PATCH 13/22] 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 e0005616b..8261efc34 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 fcff831936df44e01d5db0c893bd309fc63b5ea8 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 14:55:21 +0200 Subject: [PATCH 14/22] 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 cf3238ece..dbc5727a6 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 d74206f3621376be523417f70bee60c7b70ee1fc Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 14:59:26 +0200 Subject: [PATCH 15/22] 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 8261efc34..f42b23d61 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 16984f79b..bd7edc29d 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 68ed01c3b..447b14100 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 55217adfd..e0c6f640a 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 dcd4f4a99..b9729d3d0 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 dbc5727a6..9a5e612d5 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 c37879b15..a01ec59b6 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 f2f0e0b6c..421d5d0b1 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 11bca212e..9bdd7b359 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 7bf484852..77e7942e2 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 e2be04a72..314ecb2ed 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 ecd21c2e5..9da7e82ce 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 eaa13b080d22e9d932b73871c504b46a01cf3bae Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 15:00:04 +0200 Subject: [PATCH 16/22] 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 bd7edc29d..72ca3f455 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 c57dead264f000d590c72ec0b84177a15235d628 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 15:00:35 +0200 Subject: [PATCH 17/22] 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 9da7e82ce..aebfc55c4 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 b126e736b944760b887153cd3686b43cab403993 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 15:13:31 +0200 Subject: [PATCH 18/22] 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 b9729d3d0..7be6cb2e7 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 aebfc55c4..000000000 --- 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 dd8364e7ba53c900453d982b259646a0d438bfca Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 15:14:02 +0200 Subject: [PATCH 19/22] 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 72ca3f455..90092d687 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 a4fa63042eb8408423650c4299708b9d22beefb4 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 15:14:42 +0200 Subject: [PATCH 20/22] 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 90092d687..7e37d2a4b 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 e4afab2f9bd6b07c99957cedc95d49ed521cd084 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 15:39:23 +0200 Subject: [PATCH 21/22] 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 f42b23d61..3f204913c 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 447b14100..cf9567fb6 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 9a5e612d5..971d2b622 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 77e7942e2..2a0b3d994 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 a3e144f490182a378c0a6f8281eeb6d288d8cfc7 Mon Sep 17 00:00:00 2001 From: somethings Date: Mon, 24 Aug 2026 16:15:46 +0200 Subject: [PATCH 22/22] 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 cf9567fb6..1e06c3d48 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