diff --git a/guides/plugins/plugins/administration/module-component-management/add-custom-module.md b/guides/plugins/plugins/administration/module-component-management/add-custom-module.md index f42b23d61a..6c47f402f1 100644 --- a/guides/plugins/plugins/administration/module-component-management/add-custom-module.md +++ b/guides/plugins/plugins/administration/module-component-management/add-custom-module.md @@ -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. @@ -177,7 +181,7 @@ This should be your snippet file now: As mentioned above, Shopware 6 is looking for a `main.js` file in your plugin. Its contents get minified into a new file named after your plugin and will be moved to the `public` directory of Shopware 6 root directory. -Given this plugin would be named "AdministrationNewModule", the bundled and minified javascript code for this example would be located under `/src/Resources/public/administration/js/administration-new-module.js`, once you run the command following command in your shopware root directory: +Given this plugin would be named "AdministrationNewModule", the bundled and minified JavaScript code for this example would be located under `/src/Resources/public/administration/js/administration-new-module.js`, once you run the following command in your shopware root directory: @@ -201,6 +205,10 @@ composer run build:js:admin Your plugin has to be activated for this to work. ::: +::: info +The Administration build is the build boundary, not the whole feature lifecycle. A successful build means the generated assets are compiled; the module still depends on its entry-point import, registration, routes/components, and snippets being connected correctly at runtime. +::: + Make sure to also include that file when publishing your plugin! A copy of this file will then be put into the directory `/public/bundles/administration/administrationnewmodule/administration/js/administration-new-module.js`. diff --git a/guides/plugins/plugins/creating-plugins.md b/guides/plugins/plugins/creating-plugins.md index bd7edc29d8..b68ea59e1e 100644 --- a/guides/plugins/plugins/creating-plugins.md +++ b/guides/plugins/plugins/creating-plugins.md @@ -73,40 +73,42 @@ This is the recommended starting point when you already know what your plugin ne To generate a specific example instead of all of them, pass its option. Each option can also be answered interactively: -| Option | Generates | -| --- | --- | -| `--create-storefront-controller` | Example Storefront controller, its template, and a `routes.php` entry | -| `--create-store-api-route` | Example Store API route with abstract class and response class | -| `--create-event-subscriber` | Example event subscriber | -| `--create-command` | Example console command | -| `--create-scheduled-task` | Example scheduled task | -| `--create-admin-module` | Example Administration module with snippets | -| `--create-javascript-plugin` | Example Storefront JavaScript plugin | -| `--create-custom-fieldset` | Example custom fieldset (`custom-fields.xml`) | -| `--entities=Example,Foo` | Entity definition, entity, collection, and migration per entity (UpperCamelCase, comma-separated) | +| Option | Generates | +|----------------------------------|---------------------------------------------------------------------------------------------------| +| `--create-storefront-controller` | Example Storefront controller, its template, and a `routes.php` entry | +| `--create-store-api-route` | Example Store API route with abstract class and response class | +| `--create-event-subscriber` | Example event subscriber | +| `--create-command` | Example console command | +| `--create-scheduled-task` | Example scheduled task | +| `--create-admin-module` | Example Administration module with snippets | +| `--create-javascript-plugin` | Example Storefront JavaScript plugin | +| `--create-custom-fieldset` | Example custom fieldset (`custom-fields.xml`) | +| `--entities=Example,Foo` | Entity definition, entity, collection, and migration per entity (UpperCamelCase, comma-separated) | Every generator that needs a service definition also appends it to the plugin's service configuration in `src/Resources/config`. The generated files are placed below the plugin root. For example, the optional components use paths such as: -| Component | Typical generated location | -| --- | --- | -| Console command | `src/Command/` and `src/Resources/config/services.php` | -| Scheduled task | `src/ScheduledTask/` and `src/Resources/config/services.php` | -| Event subscriber | `src/Subscriber/` and `src/Resources/config/services.php` | -| Storefront controller | `src/Storefront/Controller/`, `src/Resources/views/`, and `src/Resources/config/routes.php` | -| Administration module | `src/Resources/app/administration/` | -| Storefront JavaScript plugin | `src/Resources/app/storefront/src/` | -| Custom field set | `src/Resources/config/custom-fields.xml` | +| Component | Typical generated location | +|------------------------------|---------------------------------------------------------------------------------------------| +| Console command | `src/Command/` and `src/Resources/config/services.php` | +| Scheduled task | `src/ScheduledTask/` and `src/Resources/config/services.php` | +| Event subscriber | `src/Subscriber/` and `src/Resources/config/services.php` | +| Storefront controller | `src/Storefront/Controller/`, `src/Resources/views/`, and `src/Resources/config/routes.php` | +| Administration module | `src/Resources/app/administration/` | +| Storefront JavaScript plugin | `src/Resources/app/storefront/src/` | +| Custom field set | `src/Resources/config/custom-fields.xml` | Treat generated examples as starting points. Selecting an option can create several related files and service definitions; deleting only one file later can leave broken -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 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 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. diff --git a/guides/plugins/plugins/framework/store-api/add-store-api-route.md b/guides/plugins/plugins/framework/store-api/add-store-api-route.md index 447b14100f..94273eed65 100644 --- a/guides/plugins/plugins/framework/store-api/add-store-api-route.md +++ b/guides/plugins/plugins/framework/store-api/add-store-api-route.md @@ -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 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 // /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-base-guide.md b/guides/plugins/plugins/plugin-base-guide.md index b9729d3d02..7be6cb2e7b 100644 --- a/guides/plugins/plugins/plugin-base-guide.md +++ b/guides/plugins/plugins/plugin-base-guide.md @@ -31,6 +31,8 @@ Most steps above can be generated instead of written by hand: * `bin/console plugin:create` scaffolds the plugin; see the [Creating Plugins guide](creating-plugins.md) * The [Shopware 6 Toolbox plugin](../../development/tooling/shopware-toolbox.md) generates plugins, subscribers, scheduled tasks, migrations, and Administration modules from PHPStorm, using file templates you can adapt to your own conventions +When a generator creates an optional feature, the files it produces are usually parts of one piece of framework wiring. For example, a generated controller can depend on both route configuration and service registration, while a scheduled task has separate task and handler roles. The focused guides explain those pieces in context; you do not normally need to reproduce the wiring manually. + ## Upgrade readiness Design plugins so that: diff --git a/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md b/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md index 9a5e612d5a..a0e29ebe2d 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 the generated PHP class in the filesystem 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 exposing a URL until its route is imported. + ::: code-group ```php [PLUGIN_ROOT/src/Resources/config/routes.php] diff --git a/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md b/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md index 77e7942e21..8a2f7d9d05 100644 --- a/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md +++ b/guides/plugins/plugins/storefront/javascript/add-custom-javascript.md @@ -66,7 +66,9 @@ Well, and then we check if this sum is bigger or equal the total size of your we Next you have to tell Shopware that your plugin should be loaded and executed. Therefore you have to register your plugin in the PluginManager. -Shopware is automatically looking for a `main.js` file in a directory `/src/Resources/app/storefront/src`, which then will be loaded automatically. Consider this to be your main storefront JavaScript entrypoint. +Shopware is automatically looking for a `main.js` file in a directory `/src/Resources/app/storefront/src`, which then will be loaded automatically. Consider this to be your main storefront JavaScript entry point. + +The entry point, plugin class, and optional DOM selector form one runtime chain. A generator creates these pieces together: `main.js` imports and registers the class, the selector connects it to rendered markup when one is used, and the Storefront build produces the asset the browser loads. Keeping that relationship in mind makes generated output easier to adapt without treating each file as an independent feature. 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: @@ -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]'); ```