From fcdfe8a088b2999c758ebe53acde83abf4485548 Mon Sep 17 00:00:00 2001 From: Soner Sayakci Date: Wed, 11 Feb 2026 11:33:51 +0100 Subject: [PATCH] feat: use autowire for configuration --- guides/plugins/plugins/bundle.md | 4 +- .../checkout/cart/add-cart-discounts.md | 14 +- .../plugins/checkout/cart/add-cart-items.md | 34 +--- .../checkout/cart/add-cart-validator.md | 19 +-- .../checkout/cart/change-price-of-item.md | 32 ++-- .../cart/customize-price-calculation.md | 23 +-- .../plugins/checkout/cart/tax-provider.md | 17 +- .../document/add-custom-document-type.md | 42 +---- .../checkout/payment/add-payment-plugin.md | 158 ++++-------------- .../payment/customize-payment-provider.md | 30 +--- .../content/cms/add-data-to-cms-elements.md | 21 +-- .../plugins/content/mail/add-data-to-mails.md | 51 +----- .../media/add-custom-file-extension.md | 30 +--- ...-media-files-referenced-in-your-plugins.md | 24 +-- .../plugins/content/seo/add-custom-seo-url.md | 57 +------ .../plugins/content/seo/extend-robots-txt.md | 79 +-------- .../sitemap/add-custom-sitemap-entries.md | 32 +--- .../content/sitemap/modify-sitemap-entries.md | 31 +--- .../implementing-your-own-stock-storage.md | 35 +--- ...stock-information-from-different-source.md | 31 +--- .../fetching-data-from-entity-selection.md | 37 +--- .../add-complex-data-to-existing-entities.md | 68 +------- .../data-handling/add-custom-complex-data.md | 20 +-- .../data-handling/add-data-translations.md | 23 +-- .../data-handling/entities-via-attributes.md | 16 +- .../framework/data-handling/reading-data.md | 19 +-- .../replacing-associated-data.md | 20 +-- .../data-handling/using-database-events.md | 18 +- .../framework/data-handling/writing-data.md | 20 +-- .../extension/creating-custom-extension.md | 18 +- .../framework/filesystem/filesystem.md | 57 ++++--- .../framework/flow/add-flow-builder-action.md | 21 +-- .../flow/add-flow-builder-trigger.md | 10 +- .../framework/rule/add-custom-rules.md | 4 +- .../store-api/override-existing-route.md | 26 +-- .../add-custom-commands.md | 37 +--- .../plugin-fundamentals/add-custom-service.md | 77 ++------- .../plugin-fundamentals/add-scheduled-task.md | 33 +--- .../plugin-fundamentals/adjusting-service.md | 31 +--- .../dependency-injection.md | 32 +--- .../listening-to-events.md | 30 +--- .../use-plugin-configuration.md | 19 +-- .../storefront/add-cookie-to-manager.md | 26 +-- .../storefront/add-custom-controller.md | 39 +++-- .../plugins/storefront/add-custom-page.md | 44 ++--- .../storefront/add-custom-twig-function.md | 18 +- .../storefront/add-data-to-storefront-page.md | 47 +----- .../add-dynamic-content-via-ajax-calls.md | 19 +-- .../add-scss-variables-via-subscriber.md | 54 +----- 49 files changed, 312 insertions(+), 1335 deletions(-) diff --git a/guides/plugins/plugins/bundle.md b/guides/plugins/plugins/bundle.md index d1ec4876b2..6bab7bb85d 100644 --- a/guides/plugins/plugins/bundle.md +++ b/guides/plugins/plugins/bundle.md @@ -33,7 +33,7 @@ project-root/ │ │ └── Migration1234567890YourMigration.php │ └── Resources/ │ ├── config/ -│ │ ├── services.xml +│ │ ├── services.php │ │ └── routes.xml │ ├── views/ │ │ └── storefront/ @@ -92,7 +92,7 @@ App\YourBundleName\YourBundleName::class => ['all' => true], ## Adding services, twig templates, routes, theme, etc You can add services, twig templates, routes, etc. to your bundle like you would do in a plugin. -Just create `Resources/config/services.xml` and `Resources/config/routes.xml` files or `Resources/views` for twig templates. +Just create `Resources/config/services.php` and `Resources/config/routes.xml` files or `Resources/views` for twig templates. The bundle will be automatically detected and the files will be loaded. To mark your bundle as a theme, it's enough to implement the `Shopware\Core\Framework\ThemeInterface` interface in your bundle class. diff --git a/guides/plugins/plugins/checkout/cart/add-cart-discounts.md b/guides/plugins/plugins/checkout/cart/add-cart-discounts.md index fa178d6693..d552e0b977 100644 --- a/guides/plugins/plugins/checkout/cart/add-cart-discounts.md +++ b/guides/plugins/plugins/checkout/cart/add-cart-discounts.md @@ -39,7 +39,9 @@ use Shopware\Core\Checkout\Cart\Price\PercentagePriceCalculator; use Shopware\Core\Checkout\Cart\Price\Struct\PercentagePriceDefinition; use Shopware\Core\Checkout\Cart\Rule\LineItemRule; use Shopware\Core\System\SalesChannel\SalesChannelContext; +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; +#[AutoconfigureTag('shopware.cart.processor', ['priority' => 4500])] class ExampleProcessor implements CartProcessorInterface { private PercentagePriceCalculator $calculator; @@ -120,4 +122,14 @@ Shopware comes with a called `LineItemRule`, which requires two parameters: After adding the definition to the line item, we have to calculate the current price of the discount. Therefore we can use the `PercentagePriceCalculator` of the core. The last step is to add the discount to the new cart which is provided as `Cart $toCalculate`. -That's it for the main code of our custom `CartProcessor`. Now we only have to register it in our `services.xml` using the tag `shopware.cart.processor` and priority `4500`, which is used to get access to the calculation after the [product processor](https://github.com/shopware/shopware/blob/v6.3.4.1/src/Core/Checkout/DependencyInjection/cart.xml#L223-L231) handled the products. +That's it for the main code of our custom `CartProcessor`. Now we only have to tag it with `shopware.cart.processor` and priority `4500`, which is used to get access to the calculation after the [product processor](https://github.com/shopware/shopware/blob/v6.3.4.1/src/Core/Checkout/DependencyInjection/cart.xml#L223-L231) handled the products. This is done by adding the `#[AutoconfigureTag]` PHP attribute directly to the class: + +```php +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; + +#[AutoconfigureTag('shopware.cart.processor', ['priority' => 4500])] +class ExampleProcessor implements CartProcessorInterface +{ + // ... +} +``` diff --git a/guides/plugins/plugins/checkout/cart/add-cart-items.md b/guides/plugins/plugins/checkout/cart/add-cart-items.md index e2affd8092..e1b5309adb 100644 --- a/guides/plugins/plugins/checkout/cart/add-cart-items.md +++ b/guides/plugins/plugins/checkout/cart/add-cart-items.md @@ -98,22 +98,7 @@ If you now call the route `/cartAdd`, it should add the product with the ID `myE Sometimes you really want to have a custom line item handler, e.g. for your own new entity, such as a bundle entity or alike. For that case, you can create your own line item handler, which will then be available in the `LineItemFactoryRegistry` as a valid `type` option. -You need to create a new class which implements the interface `\Shopware\Core\Checkout\Cart\LineItemFactoryHandler\LineItemFactoryInterface` and it needs to be registered in the DI container with the tag `shopware.cart.line_item.factory`. - -```xml -// /src/Resources/config/services.xml - - - - - - - - - -``` +You need to create a new class which implements the interface `\Shopware\Core\Checkout\Cart\LineItemFactoryHandler\LineItemFactoryInterface`. With `autoconfigure` enabled, the class is automatically tagged with `shopware.cart.line_item.factory` because it implements `LineItemFactoryInterface` — no additional configuration or attributes are needed. Let's first have a look at an example handler: @@ -166,7 +151,7 @@ Implementing the `LineItemFactoryInterface` will force you to also implement thr Here you can define which properties of your line item may actually be updated. E.g. if you really want property X to contain "Y", you can do so here. -Now you'll need to add a processor for your type. Otherwise your item won't be persisted in the cart. A simple processor for our ExampleHandler could look like this: +Now you'll need to add a processor for your type. Otherwise your item won't be persisted in the cart. While `CartProcessorInterface` is autoconfigured, we use `#[AutoconfigureTag]` here to set a custom `priority`. A simple processor for our ExampleHandler could look like this: ```php // /Core/Checkout/Cart/ExampleProcessor.php @@ -180,7 +165,9 @@ use Shopware\Core\Checkout\Cart\CartBehavior; use Shopware\Core\Checkout\Cart\CartProcessorInterface; use Shopware\Core\System\SalesChannel\SalesChannelContext; use Shopware\Core\Checkout\Cart\LineItem\CartDataCollection; +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; +#[AutoconfigureTag('shopware.cart.processor', ['priority' => 4800])] class ExampleProcessor implements CartProcessorInterface { @@ -199,19 +186,6 @@ As you can see, this processor takes an "original cart" as an input and adds all Of course you can use processors to do much more than this. Have a look at [adding cart processors and collectors](./add-cart-processor-collector). -Now register this processor in your `services.xml` like this: - -```html -// /Resources/config/services.xml -... - - ... - - - - -``` - And that's it. You should now be able to create line items of type `example`. ## Adding nested line item diff --git a/guides/plugins/plugins/checkout/cart/add-cart-validator.md b/guides/plugins/plugins/checkout/cart/add-cart-validator.md index 940c78b434..74a7004fa6 100644 --- a/guides/plugins/plugins/checkout/cart/add-cart-validator.md +++ b/guides/plugins/plugins/checkout/cart/add-cart-validator.md @@ -72,24 +72,7 @@ Important to note is the `return` statement afterwards. If you wouldn't return h #### Registering the validator -One more thing to do is to register your new validator to the [dependency injection container](../../plugin-fundamentals/dependency-injection). - -Your validator has to be registered using the tag `shopware.cart.validator`: - -```xml -// /src/Resources/config/services.xml - - - - - - - - - -``` +With `autoconfigure` enabled, the validator is automatically registered because it implements `CartValidatorInterface` — no additional configuration or attributes are needed. ### Adding the custom cart error diff --git a/guides/plugins/plugins/checkout/cart/change-price-of-item.md b/guides/plugins/plugins/checkout/cart/change-price-of-item.md index 49cbce65b6..9e407ccd61 100644 --- a/guides/plugins/plugins/checkout/cart/change-price-of-item.md +++ b/guides/plugins/plugins/checkout/cart/change-price-of-item.md @@ -152,7 +152,10 @@ use Shopware\Core\Checkout\Cart\LineItem\LineItem; use Shopware\Core\Checkout\Cart\Price\QuantityPriceCalculator; use Shopware\Core\Checkout\Cart\Price\Struct\QuantityPriceDefinition; use Shopware\Core\System\SalesChannel\SalesChannelContext; +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; +#[AutoconfigureTag('shopware.cart.processor', ['priority' => 4500])] +#[AutoconfigureTag('shopware.cart.collector', ['priority' => 4500])] class OverwritePriceCollector implements CartDataCollectorInterface, CartProcessorInterface { private QuantityPriceCalculator $calculator; @@ -259,27 +262,20 @@ Do not query the database in the `process` method. Make sure to always use a col ### Registering to DI container -One last thing, we need to register our processor and collector to the DI container. Our collector / processor has to be registered using the two tags `shopware.cart.processor` and `shopware.cart.collector`. +One last thing, we need to tag our processor and collector with `shopware.cart.processor` and `shopware.cart.collector`. This is done by adding `#[AutoconfigureTag]` PHP attributes directly to the class. The `QuantityPriceCalculator` dependency is resolved automatically via autowiring. -Let's have a look at it: +Add the following attributes to the `OverwritePriceCollector` class: -```xml -// /src/Resources/config/services.xml - - - - - - +```php +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; - - - - - - +// after product collector/processor +#[AutoconfigureTag('shopware.cart.processor', ['priority' => 4500])] +#[AutoconfigureTag('shopware.cart.collector', ['priority' => 4500])] +class OverwritePriceCollector implements CartDataCollectorInterface, CartProcessorInterface +{ + // ... +} ``` And that's it. Your processor / collector should now be working. diff --git a/guides/plugins/plugins/checkout/cart/customize-price-calculation.md b/guides/plugins/plugins/checkout/cart/customize-price-calculation.md index bca121a75a..75c2abc347 100644 --- a/guides/plugins/plugins/checkout/cart/customize-price-calculation.md +++ b/guides/plugins/plugins/checkout/cart/customize-price-calculation.md @@ -34,9 +34,13 @@ Here's an example decorated calculator: namespace Swag\BasicExample\Service; use Shopware\Core\Content\Product\SalesChannel\Price\AbstractProductPriceCalculator; +use Shopware\Core\Content\Product\SalesChannel\Price\ProductPriceCalculator; use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity; use Shopware\Core\System\SalesChannel\SalesChannelContext; +use Symfony\Component\DependencyInjection\Attribute\AsDecorator; +use Symfony\Component\DependencyInjection\Attribute\AutowireDecorated; +#[AsDecorator(decorates: ProductPriceCalculator::class)] class CustomProductPriceCalculator extends AbstractProductPriceCalculator { /** @@ -44,7 +48,7 @@ class CustomProductPriceCalculator extends AbstractProductPriceCalculator */ private AbstractProductPriceCalculator $productPriceCalculator; - public function __construct(AbstractProductPriceCalculator $productPriceCalculator) + public function __construct(#[AutowireDecorated] AbstractProductPriceCalculator $productPriceCalculator) { $this->productPriceCalculator = $productPriceCalculator; } @@ -79,22 +83,7 @@ Most likely you also want to narrow down which product's prices you want to edit ### Registering the decorator -Do not forget to actually register your decoration to the service container, otherwise it will not have any effect. - -```xml -// /src/Resources/config/services.xml - - - - - - - - - -``` +With autowiring enabled, the `#[AsDecorator]` attribute on the class is sufficient to register the decorator. No explicit service configuration is needed. The `#[AsDecorator(decorates: ProductPriceCalculator::class)]` attribute tells Symfony to automatically decorate the `ProductPriceCalculator` service with `CustomProductPriceCalculator`. ## Next steps diff --git a/guides/plugins/plugins/checkout/cart/tax-provider.md b/guides/plugins/plugins/checkout/cart/tax-provider.md index 1823b921c4..3c512dd6e5 100644 --- a/guides/plugins/plugins/checkout/cart/tax-provider.md +++ b/guides/plugins/plugins/checkout/cart/tax-provider.md @@ -83,22 +83,7 @@ class TaxProvider extends AbstractTaxProvider ## Registering the tax provider in the DI container -After you have created your tax provider, you need to register it in the DI container. To do so, you need to create a new service in the `services.xml` file and tag the service as `shopware.tax.provider`. - -```xml -// /src/Resources/config/services.xml - - - - - - - - - -``` +With `autoconfigure` enabled, the class is automatically registered because it extends `AbstractTaxProvider` — no additional configuration or attributes are needed. ## Migrate your tax provider to the database diff --git a/guides/plugins/plugins/checkout/document/add-custom-document-type.md b/guides/plugins/plugins/checkout/document/add-custom-document-type.md index 0b4bafda5d..6a82c7a70b 100644 --- a/guides/plugins/plugins/checkout/document/add-custom-document-type.md +++ b/guides/plugins/plugins/checkout/document/add-custom-document-type.md @@ -170,7 +170,7 @@ Your custom document renderer has to implement the `Shopware\Core\Checkout\Docum * `supports`: Has to return a string of the document type it supports. We named our document type "**example**", so our renderer has to return "**example**". * `render`: This needs to return the instance `Shopware\Core\Checkout\Document\Renderer\RendererResult`, which will contain the instance of `Shopware\Core\Checkout\Document\Renderer\RenderedDocument` based on each `orderId`. You will have access to the array of `DocumentGenerateOperation` which contains all respective orderIds, the context and the instance of `Shopware\Core\Checkout\Document\Renderer\DocumentRendererConfig` (additional configuration). -Furthermore, your renderer has to be registered to the [service container](../../plugin-fundamentals/dependency-injection) using the tag `document.renderer`. +With `autoconfigure` enabled, the renderer is automatically registered because it extends `AbstractDocumentRenderer` — no additional configuration or attributes are needed. Let's have a look at an example renderer: @@ -339,50 +339,16 @@ Here's what the function does: * If an error occurs, the exception is caught and the error message is added to the `RendererResult` object as an error. * The `RendererResult` object is returned. -The service definition for our custom renderer would look like this: - -```xml - - - - - - - -``` +With autowire enabled, all constructor dependencies are injected automatically. ### Adding a file type renderer Depending on the file type we either get the content with `$this->fileRendererRegistry->render()` or we need to create the content on our own. `DocumentFileRendererRegistry` acts as a central registry for document file renderers based on file extensions (e.g., .pdf, .html). It delegates the rendering of documents to the appropriate renderer implementation. -### Registering the renderer in the service container - -Now we need to register our custom `ExampleDocumentRenderer` in the service container. Create or update your `services.xml` file: - -::: code-group - -```xml [PLUGIN_ROOT/src/Resources/config/services.xml] - - - - - - - - - - - - - -``` - -::: +### Registering the renderer -Note that we're using the tag `document.renderer` to register our custom document renderer. The tag name matches what was mentioned earlier - your renderer has to be registered using the tag `document.renderer`. +With `autoconfigure` enabled, the renderer is automatically registered because it extends `AbstractDocumentRenderer`. Combined with autowire, all constructor dependencies are injected automatically. No explicit service configuration is needed. ### Adding a document type template diff --git a/guides/plugins/plugins/checkout/payment/add-payment-plugin.md b/guides/plugins/plugins/checkout/payment/add-payment-plugin.md index 4b515c128f..185307e1a3 100644 --- a/guides/plugins/plugins/checkout/payment/add-payment-plugin.md +++ b/guides/plugins/plugins/checkout/payment/add-payment-plugin.md @@ -35,23 +35,20 @@ Shopware provides you with a handy `Shopware\Core\Checkout\Payment\Cart\PaymentH ### Registering the service Before we're going to have a look at some examples, we need to register our new service to the [Dependency Injection](../../plugin-fundamentals/dependency-injection) container. -Please make sure to add the `shopware.payment.method` tag to your service definition, otherwise Shopware won't recognize your service as a payment handler. +Use the `#[AutoconfigureTag('shopware.payment.method')]` attribute on your handler class, otherwise Shopware won't recognize it as a payment handler. We'll use a class called `MyCustomPaymentHandler` here. -```xml [/src/Resources/config/services.xml] - +Use the `#[AutoconfigureTag]` attribute directly on your handler class to register it as a payment method: - +```php +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; - - - - - - +#[AutoconfigureTag('shopware.payment.method')] +class MyCustomPaymentHandler extends AbstractPaymentHandler +{ + // ... +} ``` Now, let's start with the actual examples. @@ -82,9 +79,11 @@ use Shopware\Core\Checkout\Payment\Cart\PaymentHandler\PaymentHandlerType; use Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct; use Shopware\Core\Framework\Context; use Shopware\Core\Framework\Struct\Struct; +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; +#[AutoconfigureTag('shopware.payment.method')] class MyCustomPaymentHandler extends AbstractPaymentHandler { public function __construct(private readonly OrderTransactionStateHandler $transactionStateHandler) @@ -116,22 +115,6 @@ class MyCustomPaymentHandler extends AbstractPaymentHandler } ``` -```xml [services.xml] - - - - - - - - - - - -``` - ::: This payment handler does not do a lot in the current state but is a good starting point for your custom payment handler. @@ -163,9 +146,11 @@ use Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct; use Shopware\Core\Checkout\Payment\PaymentException; use Shopware\Core\Framework\Context; use Shopware\Core\Framework\Struct\Struct; +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; +#[AutoconfigureTag('shopware.payment.method')] class MyCustomPaymentHandler extends AbstractPaymentHandler { public function __construct(private readonly OrderTransactionStateHandler $transactionStateHandler) @@ -227,22 +212,6 @@ class MyCustomPaymentHandler extends AbstractPaymentHandler } ``` -```xml [services.xml] - - - - - - - - - - - -``` - ::: Let's start with the `pay` method. You'll have to start by letting your external payment provider know where he should redirect your customer in return when the payment is done. @@ -292,9 +261,11 @@ use Shopware\Core\Framework\Struct\ArrayStruct; use Shopware\Core\Framework\Struct\Struct; use Shopware\Core\Framework\Validation\DataBag\RequestDataBag; use Shopware\Core\System\SalesChannel\SalesChannelContext; +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; +#[AutoconfigureTag('shopware.payment.method')] class MyCustomPaymentHandler extends AbstractPaymentHandler { public function __construct(private readonly OrderTransactionStateHandler $transactionStateHandler) @@ -350,7 +321,7 @@ class MyCustomPaymentHandler extends AbstractPaymentHandler // In here you should probably call your payment provider to precess the payment // $this->myPaymentProvider->processPayment($transaction); - + // afterward you should update the transaction with the new state $this->transactionStateHandler->process($transaction->getOrderTransactionId(), $context); @@ -359,22 +330,6 @@ class MyCustomPaymentHandler extends AbstractPaymentHandler } ``` -```xml [services.xml] - - - - - - - - - - - -``` - ::: @@ -406,9 +361,11 @@ use Shopware\Core\Framework\Context; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use Shopware\Core\Framework\Struct\Struct; +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; +#[AutoconfigureTag('shopware.payment.method')] class MyCustomPaymentHandler extends AbstractPaymentHandler { public function __construct( @@ -489,24 +446,6 @@ class MyCustomPaymentHandler extends AbstractPaymentHandler ``` -```xml [services.xml] - - - - - - - - - - - - - -``` - ::: As you can see, you have complete control over handling the refund request and which positions to refund. @@ -540,9 +479,11 @@ use Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct; use Shopware\Core\Checkout\Payment\PaymentException; use Shopware\Core\Framework\Context; use Shopware\Core\Framework\Struct\Struct; +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; +#[AutoconfigureTag('shopware.payment.method')] class MyCustomPaymentHandler extends AbstractPaymentHandler { public function __construct(private readonly OrderTransactionStateHandler $transactionStateHandler) @@ -567,16 +508,16 @@ class MyCustomPaymentHandler extends AbstractPaymentHandler // In here you should probably call your payment provider to create a billing agreement // $this->myPaymentProvider->createBillingAgreement($transaction); } - + // Don't forget to capture the initial payment as well // $this->myPaymentProvider->processPayment($transaction); - + // afterward you should update the transaction with the new state $this->transactionStateHandler->process($transaction->getOrderTransactionId(), $context); return null; } - + /** * call your PSP here for capturing a recurring payment * a valid billing agreement between the customer and the PSP should usually already be in place @@ -602,22 +543,6 @@ class MyCustomPaymentHandler extends AbstractPaymentHandler ``` -```xml [services.xml] - - - - - - - - - - - -``` - ::: @@ -776,39 +701,20 @@ extend the `Shopware\Core\Checkout\Payment\Cart\PaymentHandler\AbstractPaymentHa | `RecurringPaymentHandlerInterface` | `recurring` | `PaymentHandlerType::RECURRING` | | `RefundPaymentHandlerInterface` | `refund` | `PaymentHandlerType::REFUND` | -### New single tag for payment handlers +### New single attribute for payment handlers -Your payment handler should now have a single tag in the service definition: `shopware.payment.method`. -Remove any other occurrences of the following tags: -`shopware.payment.method.sync`, `shopware.payment.method.async`, `shopware.payment.method.prepared`, `shopware.payment.method.recurring`, `shopware.payment.method.refund`. +Your payment handler should now have the `#[AutoconfigureTag('shopware.payment.method')]` attribute. -::: code-group +```php +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; -```xml [services.xml] - - - - - - - - - - - - - - - - - - +#[AutoconfigureTag('shopware.payment.method')] +class MyCustomPaymentHandler extends AbstractPaymentHandler +{ + // Remove any other payment handler tags - only the single shopware.payment.method attribute is needed +} ``` -::: - ### Prepared payments In the past, you would have to implement the `validate` and `capture` methods when dealing with the `PreparedPaymentHandlerInterface`. diff --git a/guides/plugins/plugins/checkout/payment/customize-payment-provider.md b/guides/plugins/plugins/checkout/payment/customize-payment-provider.md index 486d7e2d17..616bdb3ec2 100644 --- a/guides/plugins/plugins/checkout/payment/customize-payment-provider.md +++ b/guides/plugins/plugins/checkout/payment/customize-payment-provider.md @@ -24,11 +24,10 @@ First, we create a new class that extends from the provider we want to customise In this example we customise the class `Shopware\Core\Checkout\Payment\Cart\PaymentHandler\DebitPayment` and name our class `ExampleDebitPayment`. The constructor has to accept an instance of `OrderTransactionStateHandler` like the original service and additionally an instance of `DebitPayment` that we want to decorate. -After we've created our customized payment provider class, we have to register it to the DI-container via the `services.xml`. +With autowiring enabled, the `#[AsDecorator]` attribute on the class is sufficient to register the decorator. No explicit service configuration is needed. -::: code-group - -```php [ExampleDebitPayment.php] +```php +// /src/Service/ExampleDebitPayment.php decorated = $decorated; @@ -65,21 +67,3 @@ class ExampleDebitPayment extends DebitPayment } } ``` - -```xml [services.xml] - - - - - - - - - - - -``` - -::: diff --git a/guides/plugins/plugins/content/cms/add-data-to-cms-elements.md b/guides/plugins/plugins/content/cms/add-data-to-cms-elements.md index b90d11d5fd..7f56a18dcd 100644 --- a/guides/plugins/plugins/content/cms/add-data-to-cms-elements.md +++ b/guides/plugins/plugins/content/cms/add-data-to-cms-elements.md @@ -60,26 +60,7 @@ In the previous [example](add-cms-element) we added a CMS element with the name As you can see the `getType` method of our custom resolver reflects that name by returning the `dailymotion` string. This resolver is called every time for an element of the type `dailymotion`. -To register our custom resolver to the service container, we have to register it in the `services.xml` file in our plugin. - -::: code-group - -```xml [PLUGIN_ROOT/src/Resources/config/services.xml] - - - - - - - - - - -``` - -::: +With `autoconfigure` enabled, the class is automatically registered because it extends `AbstractCmsElementResolver` — no additional configuration or attributes are needed. ### Collect data diff --git a/guides/plugins/plugins/content/mail/add-data-to-mails.md b/guides/plugins/plugins/content/mail/add-data-to-mails.md index cf117e5c11..4c4e9fddc7 100644 --- a/guides/plugins/plugins/content/mail/add-data-to-mails.md +++ b/guides/plugins/plugins/content/mail/add-data-to-mails.md @@ -35,9 +35,12 @@ So let's do that, here's an example of a decorated mail service: namespace Swag\BasicExample\Service; use Shopware\Core\Content\Mail\Service\AbstractMailService; +use Shopware\Core\Content\Mail\Service\MailService; use Shopware\Core\Framework\Context; +use Symfony\Component\DependencyInjection\Attribute\AsDecorator; use Symfony\Component\Mime\Email; +#[AsDecorator(decorates: MailService::class)] class AddDataToMails extends AbstractMailService { /** @@ -74,29 +77,9 @@ In this example, we're adding `myCustomData` to the `$templateData`, so that one If we add {{ myCustomData }} to any mail template, it should then print "Example data". You can use any kind of data here, e.g. an array of data. -### Register your decorator +### Registration via attribute -Of course you still have to register the decoration to the service container. Beware of the `decorates` attribute of our service. - -Here's the respective example `services.xml`: - -::: code-group - -```xml [PLUGIN_ROOT/src/Resources/config/services.xml] - - - - - - - - - -``` - -::: +With the `#[AsDecorator]` attribute on the class and autowire enabled, the decoration is configured automatically — no explicit service configuration is needed. ## Adding data via subscriber @@ -138,26 +121,6 @@ class MyMailSubscriber implements EventSubscriberInterface ::: -### Register your event subscriber +### Automatic registration -You have to register the subscriber to the service container as well. - -Here's the respective example `services.xml`: - -::: code-group - -```xml [PLUGIN_ROOT/src/Resources/config/services.xml] - - - - - - - - - -``` - -::: +With `autoconfigure` enabled in your `services.php`, the subscriber is automatically registered because it implements `EventSubscriberInterface` — no additional configuration is needed. diff --git a/guides/plugins/plugins/content/media/add-custom-file-extension.md b/guides/plugins/plugins/content/media/add-custom-file-extension.md index ba4a44f79f..b7e2e12542 100644 --- a/guides/plugins/plugins/content/media/add-custom-file-extension.md +++ b/guides/plugins/plugins/content/media/add-custom-file-extension.md @@ -80,9 +80,6 @@ about it. What we'll be doing now, is to add a custom `TypeDetector` class which returns an `ImageType` if the extension of the file to be checked matches our type detector. Have a look at the following example: - - - ```php // /src/Core/Content/Media/TypeDetector/CustomImageTypeDetector.php 10])] class CustomImageTypeDetector implements TypeDetectorInterface { protected const SUPPORTED_FILE_EXTENSIONS = [ @@ -118,28 +117,6 @@ class CustomImageTypeDetector implements TypeDetectorInterface } ``` - - - - -```xml -// /src/Resources/config/services.xml - - - - - - - - - -``` - - - - You will have to create a new class which implements from the interface `TypeDetectorInterface`. This will come with the requirement of having a `detect` method, which will return the respective media type. @@ -154,7 +131,6 @@ Make sure to add flags to your media type, e.g. the `transparent` flag, or if it You can find all available flags in their respective media type classes, e.g. [here](https://github.com/shopware/shopware/blob/v6.4.0.0/src/Core/Content/Media/MediaType/ImageType.php#L7-L10) for the image media type. -Make sure to register your new type detector to the [Dependency injection container](../../plugin-fundamentals/dependency-injection) -by using the tag `shopware.media_type.detector`. +The `#[AutoconfigureTag('shopware.media_type.detector', ['priority' => 10])]` attribute on the class, as shown above, registers your type detector in the [Dependency injection container](../../plugin-fundamentals/dependency-injection). No additional service configuration is needed. Shopware will now recognise your new image extension and handle your new file like an image. diff --git a/guides/plugins/plugins/content/media/prevent-deletion-of-media-files-referenced-in-your-plugins.md b/guides/plugins/plugins/content/media/prevent-deletion-of-media-files-referenced-in-your-plugins.md index 9dc1f3be32..2f77803ad0 100644 --- a/guides/plugins/plugins/content/media/prevent-deletion-of-media-files-referenced-in-your-plugins.md +++ b/guides/plugins/plugins/content/media/prevent-deletion-of-media-files-referenced-in-your-plugins.md @@ -117,26 +117,4 @@ We check if there are any references to the Media IDs from the event, in the `sl Finally, we return all the IDs of Media which are used in the slider config so that they are not deleted. -Make sure to register your event subscriber to the [Dependency injection container](../../plugin-fundamentals/dependency-injection) -by using the tag `kernel.event_subscriber`. - - - - -```xml -// /src/Resources/config/services.xml - - - - - - - - - -``` - - - +With `autoconfigure` enabled in your `services.php`, the subscriber is automatically registered because it implements `EventSubscriberInterface` — no additional configuration is needed. diff --git a/guides/plugins/plugins/content/seo/add-custom-seo-url.md b/guides/plugins/plugins/content/seo/add-custom-seo-url.md index 6e5f0cca55..8934494a0c 100644 --- a/guides/plugins/plugins/content/seo/add-custom-seo-url.md +++ b/guides/plugins/plugins/content/seo/add-custom-seo-url.md @@ -176,9 +176,6 @@ For this scenario, you can make use of the Shopware built-in `SeoUrlRoute` class Let's first have a look at such an example class: - - - ```php // /src/Storefront/Framework/Seo/SeoUrlRoute/ExamplePageSeoUrlRoute.php - - - -```xml -// /src/Resources/config/services.xml - - - - - - - - - - - -``` - - - +With `autoconfigure` enabled, the class is automatically registered because it implements `SeoUrlRouteInterface` — no additional configuration or attributes are needed. The `ExampleDefinition` dependency is resolved through autowiring. Okay, so let's look through this step by step. @@ -287,7 +262,7 @@ Your custom "SeoUrlRoute" class has to implement the `SeoUrlRouteInterface`, whi Make sure to check which kind of entity has been applied to the `getMapping` method, since you don't want to provide mappings for other entities than your custom one. -It then has to be registered to the container using the tag `shopware.seo_url.route`. +With `autoconfigure` enabled, the class is automatically tagged with `shopware.seo_url.route` because it implements `SeoUrlRouteInterface`. Now that you've set up this class, there are two more things to be done, which are covered in the next sections. @@ -297,9 +272,6 @@ Every time your entity is written now, you have to let Shopware know, that you w Once again, let's have a look at an example subscriber here: - - - ```php // /src/Service/DynamicSeoUrlPageSubscriber.php - - - -```xml -// /src/Resources/config/services.xml - - - - - - - - - - - - -``` - - - +Since `DynamicSeoUrlPageSubscriber` implements `EventSubscriberInterface`, it is automatically recognized as an event subscriber through autoconfiguration. The `SeoUrlUpdater` dependency is resolved through autowiring. No manual service registration is needed. As already said, we're using the `written` event of our custom entity by providing the entities' technical name with the `.written` suffix. Everytime it is executed, we're just using the said `update` method of the `SeoUrlUpdater`. Here you'll have to provide the technical route name and the IDs of the entities, that need to be updated. And that's it for the subscriber. diff --git a/guides/plugins/plugins/content/seo/extend-robots-txt.md b/guides/plugins/plugins/content/seo/extend-robots-txt.md index 83abac59e5..a08923e195 100644 --- a/guides/plugins/plugins/content/seo/extend-robots-txt.md +++ b/guides/plugins/plugins/content/seo/extend-robots-txt.md @@ -42,9 +42,6 @@ The `RobotsDirectiveParsingEvent` is dispatched after `robots.txt` content is pa This example shows how to dynamically add restrictions for AI crawlers: - - - ```PHP - - - -```XML - - - - - - - - - - -``` - - - +The `#[AsEventListener]` attribute registers this class as an event listener automatically, so no manual service registration is needed. The `LoggerInterface` dependency is resolved through autowiring. ## Handling custom directives The `RobotsUnknownDirectiveEvent` is dispatched when an unknown directive is encountered. Use this to support vendor-specific directives or prevent warnings for known non-standard directives: - - - ```PHP - - - -```XML - - - - - - - - - -``` - - - +The `#[AsEventListener]` attribute registers this class as an event listener automatically, so no manual service registration is needed. ## Validation and parse issues You can add validation warnings or errors during parsing using the `ParseIssue` class. This example shows common validation scenarios: - - - ```PHP - - - -```XML - - - - - - - - - -``` - - - +The `#[AsEventListener]` attribute registers this class as an event listener automatically, so no manual service registration is needed. Issues are automatically logged when the `robots.txt` configuration is saved in the Administration. Use `WARNING` for recommendations and `ERROR` for critical problems that prevent proper generation. diff --git a/guides/plugins/plugins/content/sitemap/add-custom-sitemap-entries.md b/guides/plugins/plugins/content/sitemap/add-custom-sitemap-entries.md index 437eacc8ed..2d8c8e44b1 100644 --- a/guides/plugins/plugins/content/sitemap/add-custom-sitemap-entries.md +++ b/guides/plugins/plugins/content/sitemap/add-custom-sitemap-entries.md @@ -51,8 +51,7 @@ So let's get started. Adding custom URLs to the sitemap is done by adding a so-called "URL provider" to the system. This is done by adding a new class, which is extending from `Shopware\Core\Content\Sitemap\Provider\AbstractUrlProvider`. -It then has to be registered to the [service container][dependency-injection] using the tag -`shopware.sitemap_url_provider`. +With `autoconfigure` enabled, the class is automatically tagged with `shopware.sitemap_url_provider` because it extends `AbstractUrlProvider` — no additional configuration or attributes are needed. It has to provide three methods: @@ -64,9 +63,6 @@ containing an array of all URLs to be added. Let's have a look at the example class: - - - ```php // /src/Core/Content/Sitemap/Provider/CustomUrlProvider.php - - - -```xml -// /src/Resources/config/services.xml - - - - - - - - - - - - - -``` - - - - Let's go through this step by step. Start by creating a new class `CustomUrlProvider`, which is extending from the `AbstractUrlProvider`. Following are the constants `CHANGE_FREQ` and `priority` - you don't have to add those values as constants of course. diff --git a/guides/plugins/plugins/content/sitemap/modify-sitemap-entries.md b/guides/plugins/plugins/content/sitemap/modify-sitemap-entries.md index 9c2e09dcda..9a6fd7b536 100644 --- a/guides/plugins/plugins/content/sitemap/modify-sitemap-entries.md +++ b/guides/plugins/plugins/content/sitemap/modify-sitemap-entries.md @@ -30,9 +30,6 @@ the respective `UrlProvider`, e.g. the `Shopware\Core\Content\Sitemap\Provider\P Hence, let's start with creating the basic decorated class for the `ProductUrlProvider`. We'll call this class `DecoratedProductUrlProvider`: - - - ```php // /src/Core/Content/Sitemap/Provider/DecoratedProductUrlProvider.php decoratedUrlProvider = $abstractUrlProvider; } @@ -71,27 +72,7 @@ class DecoratedProductUrlProvider extends AbstractUrlProvider } ``` - - - - -```xml -// /src/Resources/config/services.xml - - - - - - - - - -``` - - - +With autowiring enabled, the `#[AsDecorator]` attribute on the class is sufficient to register the decorator. No additional service configuration is needed. Now let's get on to the two possible ways and its benefits. diff --git a/guides/plugins/plugins/content/stock/implementing-your-own-stock-storage.md b/guides/plugins/plugins/content/stock/implementing-your-own-stock-storage.md index bab281f2c3..652aa98db5 100644 --- a/guides/plugins/plugins/content/stock/implementing-your-own-stock-storage.md +++ b/guides/plugins/plugins/content/stock/implementing-your-own-stock-storage.md @@ -23,9 +23,6 @@ First, to communicate stock alterations to a third-party service, you will have * the old quantity and * the new quantity. - - - ```php // /src/Swag/Example/Service/StockStorageDecorator.php - - - -```xml -// /src/Resources/config/services.xml - - - - - - - - - -``` - - - +With autowiring enabled, the `#[AsDecorator]` attribute on the class is sufficient to register the decorator. No additional service configuration is needed. The alter method will be called when the stock of a product should be updated. The `$changes` array contains a list of `StockAlteration` instances. These objects contain the following properties/methods: diff --git a/guides/plugins/plugins/content/stock/loading-stock-information-from-different-source.md b/guides/plugins/plugins/content/stock/loading-stock-information-from-different-source.md index 95c89e4ae4..b437bd16aa 100644 --- a/guides/plugins/plugins/content/stock/loading-stock-information-from-different-source.md +++ b/guides/plugins/plugins/content/stock/loading-stock-information-from-different-source.md @@ -19,9 +19,6 @@ Here again, you will be decorating a service; therefore, it will be helpful to f For example, to load stock from a third-party API, you need to decorate `\Shopware\Core\Content\Product\Stock\AbstractStockStorage` and implement the `load` method. When products are loaded in Shopware the `load` method will be invoked with the loaded product IDs. - - - ```php // /src/Swag/Example/Service/StockStorageDecorator.php - - - -```xml -// /src/Resources/config/services.xml - - - - - - - - - -``` - - - +With autowiring enabled, the `#[AsDecorator]` attribute on the class is sufficient to register the decorator. No additional service configuration is needed. In your `load` method, you can access the product IDs from the `StockLoadRequest` instance and perform a request to your system to retrieve the data. diff --git a/guides/plugins/plugins/framework/custom-field/fetching-data-from-entity-selection.md b/guides/plugins/plugins/framework/custom-field/fetching-data-from-entity-selection.md index 6876c300aa..64f3c619d2 100644 --- a/guides/plugins/plugins/framework/custom-field/fetching-data-from-entity-selection.md +++ b/guides/plugins/plugins/framework/custom-field/fetching-data-from-entity-selection.md @@ -50,23 +50,7 @@ class ProductSubscriber implements EventSubscriberInterface } ``` -For this subscriber to work we need to register it in the service container via the `services.xml` file: - -```xml -// /src/Resources/config/services.xml - - - - - - - - - - -``` +With `autoconfigure` enabled in your `services.php`, the subscriber is automatically registered because it implements `EventSubscriberInterface` — no additional configuration is needed. Now our `ProductSubscriber` should be called every time a product is loaded, so we can resolve the custom field `custom_linked_product`. @@ -116,24 +100,7 @@ Inside the `onProductLoaded` method we can get access to the loaded product enti But, how we can load the linked product by its `id` if the custom field was set? We have to inject the product repository to achieve it. -First we update the `services.xml` and inject the product repository. - -```xml -// /src/Resources/config/services.xml - - - - - - - - - - - -``` +With `autowire` enabled in your `services.php`, the product repository is automatically injected into the constructor. Combined with `autoconfigure`, the `kernel.event_subscriber` tag is applied automatically because the class implements `EventSubscriberInterface` — no additional configuration is needed. Now we can use the product repository in our subscriber. diff --git a/guides/plugins/plugins/framework/data-handling/add-complex-data-to-existing-entities.md b/guides/plugins/plugins/framework/data-handling/add-complex-data-to-existing-entities.md index d7e1ff09d7..40e0062845 100644 --- a/guides/plugins/plugins/framework/data-handling/add-complex-data-to-existing-entities.md +++ b/guides/plugins/plugins/framework/data-handling/add-complex-data-to-existing-entities.md @@ -55,25 +55,7 @@ class CustomExtension extends EntityExtension } ``` -Now we have to register our extension via the DI-container. If you don't know how that's done in general, head over to our guide about registering a custom service [Add a custom class / service](../../plugin-fundamentals/add-custom-service) or our guide about the [dependency injection](../../plugin-fundamentals/dependency-injection). - -Here's our `services.xml`: - -```xml -// /src/Resources/config/services.xml - - - - - - - - - - -``` +Since `CustomExtension` extends `EntityExtension`, it is automatically detected and registered by Symfony's `autoconfigure` feature in your `services.php` — no explicit service registration or tagging is needed. If you don't know how that's done in general, head over to our guide about registering a custom service [Add a custom class / service](../../plugin-fundamentals/add-custom-service) or our guide about the [dependency injection](../../plugin-fundamentals/dependency-injection). ### Adding a field with a database @@ -177,27 +159,7 @@ The last field is the inverse side of the `OneToOneAssociationField`. The first The fourth parameter is the class of the associated definition, the `ProductDefinition` in this case. The last parameter, once again, defines the autoloading. In this example, the product definition will **not** be loaded, when you're just trying to load this extension entity. Yet, the extension entity will always automatically be loaded when the product entity is loaded, just like we defined earlier. -Of course, this new definition also needs to be registered to the DI container: - -```xml -// /src/Resources/config/services.xml - - - - - - - - - - - - - - -``` +With `autoconfigure` enabled in your `services.php`, both `CustomExtension` (extending `EntityExtension`) and `ExampleExtensionDefinition` (extending `EntityDefinition`) are automatically detected and registered — no explicit service registration or tagging is needed. #### Adding the new database table @@ -305,23 +267,7 @@ We're registering to the `ProductEvents::PRODUCT_LOADED_EVENT` event, which is f Please note that its second parameter, the actual value, has to be a struct and not just a string or other kind of scalar value. -After we've created our subscriber, we have to adjust our `services.xml` to register it. Below you can find our `services.xml`. - -```xml -// /src/Resources/config/services.xml - - - - - - - - - - -``` +After we've created our subscriber, it needs to be registered. With `autoconfigure` enabled in your `services.php`, the subscriber is automatically registered because it implements `EventSubscriberInterface` — no additional configuration is needed. ## Entity extension vs. Custom fields @@ -361,10 +307,4 @@ class MyBulkExtension extends BulkEntityExtension Each yield defines the entity name which should be extended and the array value defines the fields which should be added. In this example, the `product` and `category` entities are extended. -You must also register the extension in your `services.xml` file and tag it with `shopware.bulk.entity.extension`. - -```xml - - - -``` +Since `MyBulkExtension` extends `BulkEntityExtension`, it is automatically detected and registered by Symfony's `autoconfigure` feature in your `services.php` — no explicit service registration or tagging is needed. diff --git a/guides/plugins/plugins/framework/data-handling/add-custom-complex-data.md b/guides/plugins/plugins/framework/data-handling/add-custom-complex-data.md index bd8add32d3..2febe1c0ee 100644 --- a/guides/plugins/plugins/framework/data-handling/add-custom-complex-data.md +++ b/guides/plugins/plugins/framework/data-handling/add-custom-complex-data.md @@ -163,25 +163,9 @@ Another thing to note is the `addFlags` call on the `IdField`. Those flags are l If you want to know more about the flags and how to use them, head over to our guide on how to use flags [Using flags](using-flags). -All that's left to do now, is to introduce your `ExampleDefinition` to Shopware by registering your class in your `services.xml` file and by using the `shopware.entity.definition` tag, because Shopware is looking for definitions this way. If your plugin does not have a `services.xml` file yet or you don't know how that's done, head over to our guide about registering a custom service [Add a custom class / service](../../plugin-fundamentals/add-custom-service) or our guide about the [Dependency injection](../../plugin-fundamentals/dependency-injection). - -Here's the `services.xml` as it should look like: - -```xml - - - - - - - - - -``` +All that's left to do now, is to introduce your `ExampleDefinition` to Shopware. Since the class extends `EntityDefinition`, it is automatically detected and registered by Symfony's `autoconfigure` feature in your `services.php` — no explicit service registration or tagging is needed. If your plugin does not have a `services.php` file yet or you don't know how that's done, head over to our guide about registering a custom service [Add a custom class / service](../../plugin-fundamentals/add-custom-service) or our guide about the [Dependency injection](../../plugin-fundamentals/dependency-injection). -Please note the tag for your definition and the respective `entity` attribute, which has to contain the technical name of your entity, which you provided in your entity definition. In this case this must be `swag_example`. +The entity name used for the repository service ID (e.g., `swag_example.repository`) is derived from the `getEntityName()` method of your definition class. And basically that's it already for your definition class. Theoretically you could start using your entity now by injecting the `swag_example.repository` service to other services and start working with the repository, e.g. to [read data](reading-data) or to [write data](writing-data). diff --git a/guides/plugins/plugins/framework/data-handling/add-data-translations.md b/guides/plugins/plugins/framework/data-handling/add-data-translations.md index 73ada1fd6f..756c118441 100644 --- a/guides/plugins/plugins/framework/data-handling/add-data-translations.md +++ b/guides/plugins/plugins/framework/data-handling/add-data-translations.md @@ -133,28 +133,7 @@ class ExampleTranslationDefinition extends EntityTranslationDefinition As you can see, we've implemented a `StringField` for the `name` column, the other fields like the `language_id` will be automatically added by the `EntityTranslationDefinition` since they are base fields of it. -All that's left to do now, is to introduce your `ExampleTranslationDefinition` to Shopware by registering your class in your `services.xml` file and by using the `shopware.entity.definition` tag, because Shopware 6 is looking for definitions this way. Note, that we have to register the translation after the entity we want to translate. - -Here's the `services.xml` as it should look like: - -```xml -// /src/Resources/config/services.xml - - - - - - - - - - - - - -``` +All that's left to do now, is to introduce your `ExampleTranslationDefinition` to Shopware. Since both `ExampleDefinition` and `ExampleTranslationDefinition` extend `EntityDefinition` (or `EntityTranslationDefinition`), they are automatically detected and registered by Symfony's `autoconfigure` feature in your `services.php` — no explicit service registration or tagging is needed. ### Entity class diff --git a/guides/plugins/plugins/framework/data-handling/entities-via-attributes.md b/guides/plugins/plugins/framework/data-handling/entities-via-attributes.md index 78cc6237b0..f3fbefb6b0 100644 --- a/guides/plugins/plugins/framework/data-handling/entities-via-attributes.md +++ b/guides/plugins/plugins/framework/data-handling/entities-via-attributes.md @@ -46,13 +46,17 @@ For example, you can add the `Field` attribute to a property to define the type ## Register the entity -To register the entity, you have to add this class to the DI container in the `services.xml` file. -This is done by adding the `shopware.entity` tag to the service definition. +To register the entity, you need to tag the class with `shopware.entity`. This is done by adding the `#[AutoconfigureTag]` PHP attribute directly to the entity class: -```xml - - - +```php +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; + +#[AutoconfigureTag('shopware.entity')] +#[EntityAttribute('example_entity', collectionClass: ExampleEntityCollection::class)] +class ExampleEntity extends Entity +{ + // ... +} ``` That's it. diff --git a/guides/plugins/plugins/framework/data-handling/reading-data.md b/guides/plugins/plugins/framework/data-handling/reading-data.md index e7552be6de..e58f92a1b5 100644 --- a/guides/plugins/plugins/framework/data-handling/reading-data.md +++ b/guides/plugins/plugins/framework/data-handling/reading-data.md @@ -27,22 +27,9 @@ Dealing with the Data Abstraction Layer is done by using the automatically gener The repository's service name follows this pattern: `entity_name.repository` For products this then would be `product.repository`, so let's do this. -```xml -// SwagBasicExample/src/Resources/config/services.xml - - - - - - - - - -``` +With `autowire` enabled in your `services.php`, the repository is automatically injected into the constructor — no additional configuration is needed. -And here's the respective class including its constructor: +Here's the service class including its constructor: ```php // SwagBasicExample/src/Service/ReadingData.php @@ -63,7 +50,7 @@ class ReadingData } ``` -So we registered a custom service called `ReadingData` and applied the repository as a constructor parameter. If you want to fetch data for another entity, just switch the `id` in the `services.xml` to whatever repository you need, e.g. `order.repository` for orders. +So we registered a custom service called `ReadingData` and applied the repository as a constructor parameter. If you want to fetch data for another entity, just change the type-hint in the constructor to the repository you need. The repository naming convention is `entity_name.repository`, e.g. `order.repository` for orders. ### Using the repository diff --git a/guides/plugins/plugins/framework/data-handling/replacing-associated-data.md b/guides/plugins/plugins/framework/data-handling/replacing-associated-data.md index b0e5d1e0ab..a340d2c82d 100644 --- a/guides/plugins/plugins/framework/data-handling/replacing-associated-data.md +++ b/guides/plugins/plugins/framework/data-handling/replacing-associated-data.md @@ -53,25 +53,9 @@ The product categories are a `ManyToMany` association and thus come with a mappi In order to delete it, we once again need its repository. The name for the entity can be found in the definition, to be precise inside of the `getEntityName` method. -So let's inject this repository into our class called `ReplacingData`: - -```xml -// SwagBasicExample/src/Resources/config/services.xml - - - - - - - - - - -``` +So let's inject this repository into our class called `ReplacingData`. With `autowire` enabled in your `services.php`, the repositories are automatically injected into the constructor — no additional configuration is needed. -Afterwards, you can just use the `delete` method on the repository, just like you did before in the [Writing data](writing-data) guide. +You can then use the `delete` method on the repository, just like you did before in the [Writing data](writing-data) guide. ```php public function replaceData(Context $context): void diff --git a/guides/plugins/plugins/framework/data-handling/using-database-events.md b/guides/plugins/plugins/framework/data-handling/using-database-events.md index 2c6278da94..6bee15d5cd 100644 --- a/guides/plugins/plugins/framework/data-handling/using-database-events.md +++ b/guides/plugins/plugins/framework/data-handling/using-database-events.md @@ -246,20 +246,4 @@ class ProductSubscriber implements EventSubscriberInterface After creating the event subscriber, you have to register it. If you don't know how that's done, head over to our guide about [Listening to events](../../plugin-fundamentals/listening-to-events). -Here's our `services.xml`: - -```xml -// /src/Resources/config/services.xml - - - - - - - - - - -``` +With `autoconfigure` enabled in your `services.php`, the subscriber is automatically registered because it implements `EventSubscriberInterface` — no additional configuration is needed. diff --git a/guides/plugins/plugins/framework/data-handling/writing-data.md b/guides/plugins/plugins/framework/data-handling/writing-data.md index 520a8a9916..9acfe287a4 100644 --- a/guides/plugins/plugins/framework/data-handling/writing-data.md +++ b/guides/plugins/plugins/framework/data-handling/writing-data.md @@ -32,23 +32,9 @@ Dealing with the Data Abstraction Layer is done by using the automatically gener The repository's service name follows this pattern: `entity_name.repository` For products this then would be `product.repository`. Additional to that, you're going to need the `tax` repository later for this guide, so let's add this as well already. -```xml -// SwagBasicExample/src/Resources/config/services.xml - - - - - - - - - - -``` +With `autowire` enabled in your `services.php`, the repositories are automatically injected into the constructor — no additional configuration is needed. -And here's the respective class including its constructor: +Here's the service class including its constructor: ```php // SwagBasicExample/src/Service/WritingData.php @@ -72,7 +58,7 @@ class WritingData } ``` -So we registered a custom service called `WritingData` and applied the repositories as a constructor parameter. If you want to fetch data for another entity, just switch the `id` in the `services.xml` to whatever repository you need, e.g. `order.repository` for orders. +So we registered a custom service called `WritingData` and applied the repositories as constructor parameters. If you want to work with another entity, just change the type-hint in the constructor to the repository you need. The repository naming convention is `entity_name.repository`, e.g. `order.repository` for orders. ### Creating data diff --git a/guides/plugins/plugins/framework/extension/creating-custom-extension.md b/guides/plugins/plugins/framework/extension/creating-custom-extension.md index 22a8804f65..1e6d77d9c6 100644 --- a/guides/plugins/plugins/framework/extension/creating-custom-extension.md +++ b/guides/plugins/plugins/framework/extension/creating-custom-extension.md @@ -232,19 +232,11 @@ class CustomProductFilterSubscriber implements EventSubscriberInterface ### 4. Register Services -```xml - - - - - - - - - - - -``` +With autowire and autoconfigure enabled in your `services.php`, both services are registered automatically: +- `CustomProductService` receives its dependencies via constructor autowiring +- `CustomProductFilterSubscriber` is tagged as `kernel.event_subscriber` automatically because it implements `EventSubscriberInterface` + +No explicit service configuration is needed. ## Advanced Extension Patterns diff --git a/guides/plugins/plugins/framework/filesystem/filesystem.md b/guides/plugins/plugins/framework/filesystem/filesystem.md index 5e3028f755..55ef658223 100644 --- a/guides/plugins/plugins/framework/filesystem/filesystem.md +++ b/guides/plugins/plugins/framework/filesystem/filesystem.md @@ -78,27 +78,42 @@ class ExampleFilesystemService } ``` -This service makes use of the private und public filesystem. As you already know, this php class has to be registered as a service in the dependency injection container. This is also the place where we define which filesystem will be handed over to the constructor. To make use of the plugin private and public files, the service definition could look like this: - -```xml -// /src/Resources/config/services.xml - - - - - - - - - - - +This service makes use of the private and public filesystem. Since both constructor arguments are of the same type (`FilesystemOperator`), autowiring alone cannot determine which filesystem to inject. You can use the `#[Autowire]` attribute to specify the service IDs explicitly: + +```php +// /src/Service/ExampleFilesystemService.php +fileSystemPrivate->read($filename); + } + + public function writePrivateFile(string $filename, string $content) { + $this->fileSystemPrivate->write($filename, $content); + } + + public function listPublicFiles(): array { + return $this->fileSystemPublic->listContents(); + } +} ``` Now, this service can be used to read or write files to the private plugin filesystem or to list all files in the public plugin filesystem. You should visit the [Flysystem API documentation](https://flysystem.thephpleague.com/docs/usage/filesystem-api/) for more information. diff --git a/guides/plugins/plugins/framework/flow/add-flow-builder-action.md b/guides/plugins/plugins/framework/flow/add-flow-builder-action.md index bfc7f40e32..7c9199481f 100644 --- a/guides/plugins/plugins/framework/flow/add-flow-builder-action.md +++ b/guides/plugins/plugins/framework/flow/add-flow-builder-action.md @@ -70,7 +70,9 @@ use Shopware\Core\Content\Flow\Dispatching\StorableFlow; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; use Shopware\Core\Framework\Uuid\Uuid; use Swag\CreateTagAction\Core\Framework\Event\TagAware; +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; +#[AutoconfigureTag('flow.action', ['priority' => 600, 'key' => 'action.create.tag'])] class CreateTagAction extends FlowAction { private EntityRepository $tagRepository; @@ -138,15 +140,7 @@ As you can see, several methods are already implemented: - Use `$flow->getStore($key)` if you want to get the data from aware interfaces. E.g: `tag_id` in `TagAware`, `customer_id` from `CustomerAware` and so on. - Use `$flow->getData($key)` if you want to get the data from original events or additional data. E.g: `tag`, `customer`, `contactFormData` and so on. -You also need to register this action in the container as a service. Make sure to define a tag `` at `/src/Resources/config/services.xml`. This tag will ensure that your action is included in the response of the *`/api/_info/flow-actions.json`* API. The priority attribute will determine the order of the action in the API response. - -```XML -// /src/Resources/config/services.xml - - - - -``` +The `#[AutoconfigureTag('flow.action', ['priority' => 600, 'key' => 'action.create.tag'])]` attribute on the class, as shown above, ensures that your action is registered in the DI container and included in the response of the *`/api/_info/flow-actions.json`* API. The `priority` determines the order of the action in the API response. The `tag.repository` argument is automatically injected via autowiring. Great, your own action is created completely. Let's go to the next step. @@ -311,14 +305,7 @@ class BusinessEventCollectorSubscriber implements EventSubscriberInterface } ``` -And don't forget to register your subscriber to the container at `/src/Resources/config/services.xml`. - -```xml - - - - -``` +Since the `BusinessEventCollectorSubscriber` class implements `EventSubscriberInterface`, Symfony's autoconfigure feature automatically registers it as an event subscriber. The `BusinessEventCollector` argument is automatically injected via autowiring. No additional service configuration is needed. - Define the Event snippet diff --git a/guides/plugins/plugins/framework/flow/add-flow-builder-trigger.md b/guides/plugins/plugins/framework/flow/add-flow-builder-trigger.md index 4629c278a4..8ecbaa05e3 100644 --- a/guides/plugins/plugins/framework/flow/add-flow-builder-trigger.md +++ b/guides/plugins/plugins/framework/flow/add-flow-builder-trigger.md @@ -392,15 +392,7 @@ public static function getSubscribedEvents() } ``` -And remember to register your subscriber to the container at `/src/Resources/config/services.xml` - -```xml -// /src/Resources/config/services.xml - - - - -``` +With `autoconfigure` enabled in your `services.php`, the subscriber is automatically registered because it implements `EventSubscriberInterface` — no additional configuration is needed. The `BusinessEventCollector` argument is injected automatically via `autowire`. Well done, you have successfully created your own flow trigger. diff --git a/guides/plugins/plugins/framework/rule/add-custom-rules.md b/guides/plugins/plugins/framework/rule/add-custom-rules.md index 0b1516632d..0b5f3c16e1 100644 --- a/guides/plugins/plugins/framework/rule/add-custom-rules.md +++ b/guides/plugins/plugins/framework/rule/add-custom-rules.md @@ -17,7 +17,7 @@ This example will introduce a new rule, which checks if it is the first monday o In order to add your own custom rules for your plugin, you first need a plugin as base. Therefore, you can refer to the [Plugin Base Guide](../../plugin-base-guide). -You also should be familiar with the [Dependency Injection container](../../plugin-fundamentals/dependency-injection) as this is used to register your custom rule. +You also should be familiar with the [Dependency Injection container](../../plugin-fundamentals/dependency-injection) as this is used to configure your services. It might be helpful to gather some general understanding about the concept of [Rules](../../../../../concepts/framework/rule) as well. @@ -106,7 +106,7 @@ As you can see, several methods are already implemented: * `match`: This checks whether the rule applies. Accordingly, a boolean is returned whether the rule applies or not. * `getConstraints`: This method returns an array of the possible fields and its types. You could also return the `NotBlank` class here, to require this field. -After we've created our rule class, we have to register it in our `services.xml` and tag it as `shopware.rule.definition`. +With `autoconfigure` enabled in your `services.php`, any class extending `Rule` is automatically tagged as `shopware.rule.definition` — no explicit service registration is needed. Please keep in mind: The variables to be used in the rule have to be 'protected' and not 'private', otherwise they won't work properly. ::: warning diff --git a/guides/plugins/plugins/framework/store-api/override-existing-route.md b/guides/plugins/plugins/framework/store-api/override-existing-route.md index f3fef7fa48..4fe9ab447a 100644 --- a/guides/plugins/plugins/framework/store-api/override-existing-route.md +++ b/guides/plugins/plugins/framework/store-api/override-existing-route.md @@ -32,8 +32,11 @@ use Shopware\Core\Framework\Routing\StoreApiRouteScope; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use Shopware\Core\System\SalesChannel\SalesChannelContext; +use Symfony\Component\DependencyInjection\Attribute\AsDecorator; +use Symfony\Component\DependencyInjection\Attribute\AutowireDecorated; use Symfony\Component\Routing\Attribute\Route; +#[AsDecorator(decorates: ExampleRoute::class)] #[Route(defaults: [PlatformRequest::ATTRIBUTE_ROUTE_SCOPE => [StoreApiRouteScope::ID]])] class ExampleRouteDecorator extends AbstractExampleRoute { @@ -41,7 +44,7 @@ class ExampleRouteDecorator extends AbstractExampleRoute private AbstractExampleRoute $decorated; - public function __construct(EntityRepository $exampleRepository, AbstractExampleRoute $exampleRoute) + public function __construct(EntityRepository $exampleRepository, #[AutowireDecorated] AbstractExampleRoute $exampleRoute) { $this->exampleRepository = $exampleRepository; $this->decorated = $exampleRoute; @@ -70,23 +73,4 @@ As you can see, our decorated route has to extend from the `AbstractExampleRoute ## Registering route -Last, we have to register the decorated route to the DI-container. The `ExampleRouteDecorator` has to be registered after the `ExampleRoute` with the attribute `decorated` which points to the `ExampleRoute`. For the second argument we have to use the `ExampleRouteDecorator.inner`. - -```xml -// /src/Resources/config/services.xml - - - - - - ... - - - - - - - -``` +With autowiring enabled, the `#[AsDecorator]` attribute on the class is sufficient to register the decorator. No explicit service configuration is needed. The `#[AsDecorator(decorates: ExampleRoute::class)]` attribute tells Symfony to automatically decorate the `ExampleRoute` service with `ExampleRouteDecorator`. diff --git a/guides/plugins/plugins/plugin-fundamentals/add-custom-commands.md b/guides/plugins/plugins/plugin-fundamentals/add-custom-commands.md index f7d517cc72..47271db010 100644 --- a/guides/plugins/plugins/plugin-fundamentals/add-custom-commands.md +++ b/guides/plugins/plugins/plugin-fundamentals/add-custom-commands.md @@ -7,7 +7,7 @@ nav: # Add Custom CLI Commands -To ease development tasks, Shopware contains the Symfony commands functionality. This allows \(plugin-\) developers to define new commands executable via the Symfony console at `bin/console`. The best thing about commands is, that they're more than just simple standalone PHP scripts - they integrate into Symfony and Shopware, so you've got access to all the functionality offered by both of them. +To ease development tasks, Shopware contains the Symfony commands functionality. This allows (plugin-) developers to define new commands executable via the Symfony console at `bin/console`. The best thing about commands is, that they're more than just simple standalone PHP scripts - they integrate into Symfony and Shopware, so you've got access to all the functionality offered by both of them. Creating a command for Shopware 6 via a plugin works exactly like you would add a command to Symfony. Make sure to have a look at the Symfony commands guide: @@ -19,7 +19,7 @@ This guide **does not** explain how to create a new plugin for Shopware 6. Head -The main requirement here is to have a `services.xml` file loaded in your plugin. This can be achieved by placing the file into a `Resources/config` directory relative to your plugin's base class location. +The main requirement here is to have a `services.php` file loaded in your plugin. This can be achieved by placing the file into a `Resources/config` directory relative to your plugin's base class location. ::: info Refer to this video on custom **[Creating a CLI command](https://www.youtube.com/watch?v=OL_qNVLLyaI)**. Also available on our free online training ["Shopware 6 Backend Development"](https://academy.shopware.com/courses/shopware-6-backend-development-with-jisse-reitsma). @@ -27,36 +27,7 @@ Refer to this video on custom **[Creating a CLI command](https://www.youtube.com ## Registering your command -From here on, everything works exactly like in Symfony itself. Commands are recognised by Shopware, once they're tagged with the `console.command` tag in the [dependency injection](dependency-injection) container. So to register a new command, just add it to your plugin's `services.xml` and specify the `console.command` tag: - -```html - - - - - - - - -``` - -Here's a full example `services.xml` which registers your custom command: - -```xml -// /src/Resources/config/services.xml - - - - - - - - - - -``` +With `autoconfigure` enabled in your `services.php`, commands are automatically recognised by Shopware. Any class extending `Command` with the `#[AsCommand]` attribute will be registered automatically — no additional configuration is needed. Your command's class should extend from the `Symfony\Component\Console\Command\Command` class, here's an example: @@ -126,7 +97,7 @@ Available commands: feature feature:dump Creating json file with feature config for js testing and hot reloading capabilities. assets - assets:install + assets:install bundle bundle:dump Creates a json file with the configuration for each active Shopware bundle. cache diff --git a/guides/plugins/plugins/plugin-fundamentals/add-custom-service.md b/guides/plugins/plugins/plugin-fundamentals/add-custom-service.md index b50af8a6b8..2232a7e346 100644 --- a/guides/plugins/plugins/plugin-fundamentals/add-custom-service.md +++ b/guides/plugins/plugins/plugin-fundamentals/add-custom-service.md @@ -18,75 +18,32 @@ Therefore, you can refer to the [Plugin Base Guide](../plugin-base-guide). ## Adding service -For adding a custom service, you need to provide a `services.xml` file in your plugin. -Place a file with name `services.xml` into a directory called `src/Resources/config/`. +To register services in your plugin, create a `services.php` file in the `src/Resources/config/` directory. Using `autowire` and `autoconfigure`, Symfony will automatically register and configure your services. -::: code-group - -```xml [PLUGIN_ROOT/src/Resources/config/services.xml] - - - - - - - -``` - -::: - -Now you have two possibilities to add a service to your plugin. - -### Using autowire and autoconfigure - -Set `autowire` and `autoconfigure` to `true` in your `services.xml` file. -Symfony will then automatically register your service. Read more about it in the [Symfony docs](https://symfony.com/doc/current/service_container.html#creating-configuring-services-in-the-container). ::: code-group -```xml [PLUGIN_ROOT/src/Resources/config/services.xml] - +```php [PLUGIN_ROOT/src/Resources/config/services.php] + +return static function (ContainerConfigurator $configurator): void { + $services = $configurator->services() + ->defaults() + ->autowire() + ->autoconfigure(); - - - - - + $services->load('Swag\\BasicExample\\', '../../../') + ->exclude('../../../{Resources,Migration}'); +}; ``` ::: Now every PHP class in the `src` directory of your plugin will be registered as a service. -The directory `Resources` and `Migration` are excluded, as they usually should not contain services. - -### Explicit declaration - -Instead of autowiring and autoconfiguring, you can also declare your service explicitly. -Use this option if you want to have more control over your service. - -::: code-group - -```xml [PLUGIN_ROOT/src/Resources/config/services.xml] - - - - - - - - -``` - -::: +The directories `Resources` and `Migration` are excluded, as they usually should not contain services. ### Actual service class @@ -115,12 +72,6 @@ By default, all services in Shopware 6 are marked as _private_. Read more about [private and public services](https://symfony.com/doc/current/service_container.html#public-versus-private-services). ::: -## Alternatives to XML - -Symfony offers two other file formats to define your services: YAML and PHP. -In Shopware, it is also possible to use one of these. -Choose the one that suits you best. - ## Next steps You have now created your own custom service. diff --git a/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md b/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md index c6b372c612..284bda91f5 100644 --- a/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md +++ b/guides/plugins/plugins/plugin-fundamentals/add-scheduled-task.md @@ -13,7 +13,7 @@ Quite often one might want to run any type of code on a regular basis, e.g. to c ## Prerequisites -This guide is built upon our [plugin base guide](../plugin-base-guide), but that one is not mandatory. Knowing how the `services.xml` file in a plugin works is also helpful, which will be taught in our guides about [Dependency Injection](dependency-injection) and [Creating a service](add-custom-service). It is shortly explained here as well though, so no worries! +This guide is built upon our [plugin base guide](../plugin-base-guide), but that one is not mandatory. Knowing how the `services.php` file in a plugin works is also helpful, which will be taught in our guides about [Dependency Injection](dependency-injection) and [Creating a service](add-custom-service). It is shortly explained here as well though, so no worries! ::: info Refer to this video on **[Adding scheduled tasks](https://www.youtube.com/watch?v=88S9P3x6wYE)**. Also available on our free online training ["Shopware 6 Backend Development"](https://academy.shopware.com/courses/shopware-6-backend-development-with-jisse-reitsma). @@ -21,36 +21,13 @@ Refer to this video on **[Adding scheduled tasks](https://www.youtube.com/watch? ## Registering scheduled task in the DI container -A `ScheduledTask` and its respective `ScheduledTaskHandler` are registered in a plugin's `services.xml`. For it to be found by Shopware 6 automatically, you need to place the `services.xml` file in a `Resources/config/` directory, relative to the location of your plugin's base class. The path could look like this: `/src/Resources/config/services.xml`. - -Here's an example `services.xml` containing a new `ScheduledTask` as well as a new `ScheduledTaskHandler`: - -```xml -// /src/Resources/config/services.xml - - - - - - - - - - - - - -``` +With `autoconfigure` enabled in your `services.php`, both `ScheduledTask` and `ScheduledTaskHandler` classes are automatically detected and registered. The `ScheduledTask` is automatically tagged with `shopware.scheduled.task`, and the `#[AsMessageHandler]` attribute on the handler takes care of message handler registration. -Note the tags required for both the task and its respective handler, `shopware.scheduled.task` and `messenger.message_handler`. Your custom task will now be saved into the database once your plugin is activated. +No explicit service configuration is required — simply create your classes and they will be picked up automatically. ## ScheduledTask and its handler -As you might have noticed, the `services.xml` file tries to find both the task itself as well as the new task handler in a directory called `Service/ScheduledTask`. This naming is up to you, Shopware 6 decided to use this name though. - -Here's the an example `ScheduledTask`: +Here's an example `ScheduledTask`: ```php // /src/Service/ScheduledTask/ExampleTask.php @@ -102,7 +79,7 @@ class ExampleTaskHandler extends ScheduledTaskHandler } ``` -The task handler, `ExampleTaskHandler` as defined previously in your `services.xml`, will be annotated with `AsMessageHandler` handling the `ExampleTask` class. In addition, the `ScheduledTaskHandler` has to extend from the class `Shopware\Core\Framework\MessageQueue\ScheduledTask\ScheduledTaskHandler`. This also comes with one method that you need to implement first: +The task handler, `ExampleTaskHandler`, is annotated with `#[AsMessageHandler]` handling the `ExampleTask` class. In addition, the `ScheduledTaskHandler` has to extend from the class `Shopware\Core\Framework\MessageQueue\ScheduledTask\ScheduledTaskHandler`. This also comes with one method that you need to implement first: * `run`: This method is executed once your scheduled task is executed. Do everything, that your task is supposed to do here. In this example, it will just create a new file. diff --git a/guides/plugins/plugins/plugin-fundamentals/adjusting-service.md b/guides/plugins/plugins/plugin-fundamentals/adjusting-service.md index c7c809b0cc..7f6adbb43e 100644 --- a/guides/plugins/plugins/plugin-fundamentals/adjusting-service.md +++ b/guides/plugins/plugins/plugin-fundamentals/adjusting-service.md @@ -21,27 +21,7 @@ Refer to this video on **[Decorating services](https://www.youtube.com/watch?v=R ## Decorating the service -First of all we have to create a new service for this example which gets decorated in the next step. Then we have to add a new service to our `services.xml` with the attribute `decorates` pointing to our service we want to decorate. Next we have to add our service decorator as argument, but we append an `.inner` to the end of the service to keep the old one as reference. - -Here's our example `services.xml`: - -```xml -// /src/Resources/config/services.xml - - - - - - - - - - - - -``` +First of all we have to create a new service for this example which gets decorated in the next step. With `autowire` and `autoconfigure` enabled, you can use the `#[AsDecorator]` attribute on your decorator class to define the decoration — no `services.php` configuration is needed. Now we have to define an abstract class because it's more beautiful and not so strict like interfaces. With an abstract class we can add new functions easier, you can read more about this at the end of this article. The abstract class has to include an abstract function called `getDecorated()` which has the return type of our instance. @@ -59,7 +39,7 @@ namespace Swag\BasicExample\Service; abstract class AbstractExampleService { - abstract public function getDecorated(): AbstractExampleService; + abstract public function getDecorated(): AbstractExampleService; abstract public function doSomething(): string; } @@ -93,7 +73,7 @@ class ExampleService extends AbstractExampleService The last step is creating our decorated service called `ExampleServiceDecorator` in this example. Our decorated service has to extend from the `AbstractExampleService` and the constructor has to accept an instance of `AbstractExampleService`. Furthermore, the `getDecorated()` function has to return the decorated service passed into the constructor. -Your service could then look like below: +The `#[AsDecorator]` attribute replaces the need for any XML or PHP service configuration for the decoration: ```php // /src/Service/ExampleServiceDecorator.php @@ -101,6 +81,9 @@ Your service could then look like below: namespace Swag\BasicExample\Service; +use Symfony\Component\DependencyInjection\Attribute\AsDecorator; + +#[AsDecorator(decorates: ExampleService::class)] class ExampleServiceDecorator extends AbstractExampleService { private AbstractExampleService $decoratedService; @@ -138,7 +121,7 @@ namespace Swag\BasicExample\Service; abstract class AbstractExampleService { - abstract public function getDecorated(): AbstractExampleService; + abstract public function getDecorated(): AbstractExampleService; abstract public function doSomething(): string; diff --git a/guides/plugins/plugins/plugin-fundamentals/dependency-injection.md b/guides/plugins/plugins/plugin-fundamentals/dependency-injection.md index dd42a95c54..d07b601479 100644 --- a/guides/plugins/plugins/plugin-fundamentals/dependency-injection.md +++ b/guides/plugins/plugins/plugin-fundamentals/dependency-injection.md @@ -28,8 +28,8 @@ It is also available on our free online training ["Shopware 6 Backend Developmen ## Injecting another service This example will be about injecting the `SystemConfigService` into our `ExampleService`. -First, we are preparing the `ExampleService` PHP class. -Add the `SystemConfigService` as parameter to the constructor of the service class. + +With autowire enabled in your `services.php`, constructor injection works automatically. Simply add the service as a parameter to your constructor — Symfony will resolve and inject it for you. ::: code-group @@ -57,30 +57,4 @@ class ExampleService ::: -### Using autowire and autoconfigure - -If you previously declared `autowire` and `autoconfigure` in your `services.xml` file, you do not need to do anything else. -The `SystemConfigService` will be injected into the `ExampleService` automatically. - -### Explicit declaration - -If you declared the service explicitly, you need to add the `SystemConfigService` as argument to the service. - -::: code-group - -```xml [PLUGIN_ROOT/src/Resources/config/services.xml] - - - - - - - - - - -``` - -::: +No additional configuration in `services.php` is required — autowire handles the injection automatically. diff --git a/guides/plugins/plugins/plugin-fundamentals/listening-to-events.md b/guides/plugins/plugins/plugin-fundamentals/listening-to-events.md index f97e196fd5..7d83ce6543 100644 --- a/guides/plugins/plugins/plugin-fundamentals/listening-to-events.md +++ b/guides/plugins/plugins/plugin-fundamentals/listening-to-events.md @@ -24,8 +24,8 @@ Also available on our free online training ["Shopware 6 Backend Development"](ht ### Plugin base class -Registering a custom subscriber requires to load a `services.xml` file with your plugin. -This is done by either placing a file with name `services.xml` into a directory called `src/Resources/config/`. +Registering a custom subscriber requires a `services.php` file loaded with your plugin. +This is done by placing a file with name `services.php` into a directory called `src/Resources/config/`. Basically, that's it already if you're familiar with [Symfony subscribers](https://symfony.com/doc/current/event_dispatcher.html#creating-an-event-subscriber). Don't worry, we got you covered here as well. @@ -103,28 +103,6 @@ class MySubscriber implements EventSubscriberInterface } ``` -Unfortunately, your subscriber is not even loaded yet - this will be done in the previously registered `services.xml` file. +### Automatic registration via autoconfigure -### Registering your subscriber via services.xml - -Registering your subscriber to Shopware 6 is also as simple as it is in Symfony. -You're simply registering your \(subscriber\) service by mentioning it in the `services.xml`. -The only difference to a normal service is that you need to add the `kernel.event_subscriber` tag to your subscriber for it to be recognized as such. - -```php -// /src/Resources/config/services.xml - - - - - - - - - - -``` - -That's it, your subscriber service is now automatically loaded at runtime, and it should start listening to the mentioned events to be dispatched. +With `autoconfigure` enabled in your `services.php`, any class implementing `EventSubscriberInterface` is automatically tagged as `kernel.event_subscriber`. No additional configuration is needed — your subscriber is ready to use as soon as it's loaded by the service container. diff --git a/guides/plugins/plugins/plugin-fundamentals/use-plugin-configuration.md b/guides/plugins/plugins/plugin-fundamentals/use-plugin-configuration.md index 75620cdfbb..6d9cc9b09d 100644 --- a/guides/plugins/plugins/plugin-fundamentals/use-plugin-configuration.md +++ b/guides/plugins/plugins/plugin-fundamentals/use-plugin-configuration.md @@ -68,24 +68,9 @@ Let's get to the important part. Reading the plugin configuration is based on th Inject this service into your subscriber using the [DI container](https://symfony.com/doc/current/service_container.html). -```xml -// /src/Resources/config/services.xml - - - - - - - - - - - -``` +With `autoconfigure` enabled in your `services.php`, the subscriber is automatically registered because it implements `EventSubscriberInterface` — no additional configuration is needed. Likewise, `autowire` takes care of injecting the `SystemConfigService` constructor argument automatically. -Note the new `argument` being provided to your subscriber. Now create a new field in your subscriber and pass in the `SystemConfigService`: +Now create a new field in your subscriber and pass in the `SystemConfigService`: ```php // /src/Subscriber/MySubscriber.php diff --git a/guides/plugins/plugins/storefront/add-cookie-to-manager.md b/guides/plugins/plugins/storefront/add-cookie-to-manager.md index 82f4115426..feb7549054 100644 --- a/guides/plugins/plugins/storefront/add-cookie-to-manager.md +++ b/guides/plugins/plugins/storefront/add-cookie-to-manager.md @@ -27,32 +27,12 @@ Adding custom cookies requires you to listen to the `CookieGroupsCollectEvent` a It is recommended to use an event listener if you're listening to a single event. If you need to react to multiple events, an event subscriber is the better choice. ::: -### Registering your event listener - -Start with creating the `services.xml` and registering your event listener. - -```xml -// /src/Resources/config/services.xml - - - - - - - - - - -``` - -In the next step we'll create the actual listener class. - ### Creating the listener We need to create a class called `CookieListener` with an `__invoke` method. This method will be executed once the `CookieGroupsCollectEvent` is dispatched. +The `#[AsEventListener]` attribute registers this class as an event listener automatically, so no manual service registration is needed. + The event object that is passed to our listener method contains the cookie groups collection, which we can use to add our custom cookies. ::: warning @@ -70,7 +50,9 @@ namespace PluginName\Listener; use Shopware\Storefront\Framework\Cookie\CookieGroupsCollectEvent; use Shopware\Core\Framework\Cookie\CookieEntry; use Shopware\Core\Framework\Cookie\CookieGroup; +use Symfony\Component\EventDispatcher\Attribute\AsEventListener; +#[AsEventListener(event: CookieGroupsCollectEvent::class)] class CookieListener { public function __invoke(CookieGroupsCollectEvent $event): void diff --git a/guides/plugins/plugins/storefront/add-custom-controller.md b/guides/plugins/plugins/storefront/add-custom-controller.md index 8b51640df3..f79f62991d 100644 --- a/guides/plugins/plugins/storefront/add-custom-controller.md +++ b/guides/plugins/plugins/storefront/add-custom-controller.md @@ -135,32 +135,35 @@ class ExampleController extends StorefrontController ::: -### Services.xml example +### Services.php example -Next, we need to register our controller in the DI-container and make it public. +Next, we need to register our controller in the DI-container and make it public. Controllers need explicit configuration because they must be public and have the container injected. ::: code-group -```xml [PLUGIN_ROOT/src/Resources/config/services.xml] - - - - - - - - - - - - +```php [PLUGIN_ROOT/src/Resources/config/services.php] +services() + ->defaults() + ->autowire() + ->autoconfigure(); + + $services->load('Swag\\BasicExample\\', '../../../') + ->exclude('../../../{Resources,Migration}'); + + $services->set(\Swag\BasicExample\Storefront\Controller\ExampleController::class) + ->public() + ->call('setContainer', [service('service_container')]); +}; ``` ::: -Please also note the `call` tag, which is necessary in order to set the DI container to the controller. +Please also note the `setContainer` call, which is necessary in order to set the DI container to the controller. ### Routes.xml example diff --git a/guides/plugins/plugins/storefront/add-custom-page.md b/guides/plugins/plugins/storefront/add-custom-page.md index 2c74792bb5..a2a44b2e4a 100644 --- a/guides/plugins/plugins/storefront/add-custom-page.md +++ b/guides/plugins/plugins/storefront/add-custom-page.md @@ -56,7 +56,7 @@ class ExampleController extends StorefrontController It has a method `examplePage`, which is accessible via the route `example-page`. This method will be responsible for loading your page later on, but we'll leave it like that for now. -Don't forget to [register your controller via the DI](add-custom-controller#services-xml-example). +Don't forget to [register your controller via the DI](add-custom-controller#services-php-example). ### Creating the pageloader @@ -144,18 +144,9 @@ It will be called `ExamplePageLoadedEvent`. The last thing to do in this method is to return your new page instance. -Remember to register your new page loader in the DI container: +### Registering the page loader -::: code-group - -```xml [PLUGIN_ROOT/src/Resources/config/services.xml] - - - - -``` - -::: +With autowire enabled, the `ExamplePageLoader` is automatically registered — its constructor dependencies (`GenericPageLoaderInterface` and `EventDispatcherInterface`) are injected automatically. No explicit service configuration is needed for the page loader. #### Adjusting the controller @@ -197,19 +188,30 @@ class ExampleController extends StorefrontController Note, that we've added the page to the template variables. -#### Adjusting the services.xml +#### Registering the controller -In addition, it is necessary to pass the argument with the ID of the `ExamplePageLoader` class to the [configuration](add-custom-controller#services-xml-example) of the controller service in the `services.xml`. +The controller still needs explicit registration because it must be public and have the container injected. The `ExamplePageLoader` is injected via autowire automatically. ::: code-group -```xml [PLUGIN_ROOT/src/Resources/config/services.xml] - - - - - - +```php [PLUGIN_ROOT/src/Resources/config/services.php] +services() + ->defaults() + ->autowire() + ->autoconfigure(); + + $services->load('Swag\\BasicExample\\', '../../../') + ->exclude('../../../{Resources,Migration}'); + + $services->set(\Swag\BasicExample\Storefront\Controller\ExampleController::class) + ->public() + ->call('setContainer', [service('service_container')]); +}; ``` ::: diff --git a/guides/plugins/plugins/storefront/add-custom-twig-function.md b/guides/plugins/plugins/storefront/add-custom-twig-function.md index 1875552df1..dafbd5a64b 100644 --- a/guides/plugins/plugins/storefront/add-custom-twig-function.md +++ b/guides/plugins/plugins/storefront/add-custom-twig-function.md @@ -24,11 +24,7 @@ Therefore, you can refer to the [Plugin Base Guide](../plugin-base-guide). ## Creating twig function -In the following sections, we will create and expand all necessary files for the twig function to work. -There are two such files: - -* PHP file with the twig functions itself and -* Services.xml +In the following section, we will create the PHP file for the twig function. ### Creating the twig function @@ -65,17 +61,7 @@ class SwagCreateMd5Hash extends AbstractExtension ::: -Of course, you can do everything in the `createMd5Hash` function that PHP can do, but the `service.xml` handles registration of the service in the DI container. - -::: code-group - -```xml [PLUGIN_ROOT/src/Resources/config/services.xml] - - - - - -``` +Of course, you can do everything in the `createMd5Hash` function that PHP can do. Since the class extends `AbstractExtension`, Symfony's autoconfigure feature automatically recognizes it as a Twig extension and registers it in the DI container. No additional service configuration is needed. Once done, you can access this `TwigFunction` within your plugin. diff --git a/guides/plugins/plugins/storefront/add-data-to-storefront-page.md b/guides/plugins/plugins/storefront/add-data-to-storefront-page.md index f5dc9011ae..b1abe35064 100644 --- a/guides/plugins/plugins/storefront/add-data-to-storefront-page.md +++ b/guides/plugins/plugins/storefront/add-data-to-storefront-page.md @@ -68,15 +68,9 @@ class AddDataToPage implements EventSubscriberInterface } ``` -The next thing we need to do is register our subscriber in the DI-Container and tag it as an event subscriber: +The subscriber is automatically registered by autoconfigure: -```xml -// Resources/config/services.xml - - - - -``` +With `autoconfigure` enabled in your `services.php`, the subscriber is automatically registered because it implements `EventSubscriberInterface` — no additional configuration is needed. ### Adding data to the page @@ -170,19 +164,7 @@ class ProductCountRoute extends AbstractProductCountRoute ### Register route class -```xml - - - - - - - - - -``` +With autowire enabled in your `services.php`, the `ProductCountRoute` is registered automatically. Its `product.repository` dependency is injected via constructor autowiring. The routes.xml according to our guide for [adding store-api routes](../framework/store-api/add-store-api-route) should look like this. @@ -272,26 +254,11 @@ Completely new should only be the last line: `$event->getPagelet()->addExtension Basically what you're doing here, is to fetch the actual pagelet instance from the event and add the data to the template. This data will then be available via the name `product_count`, but we'll get to that in the next section. -Now you only have to adjust your service definition to inject the productCountRoute: +Both services are configured automatically via autowire and autoconfigure: -```xml - - - - - - - - - - - - - - -``` +With autowire and autoconfigure enabled, both services are registered automatically: +- `ProductCountRoute` receives `product.repository` via autowire +- `AddDataToPage` is tagged as an event subscriber via autoconfigure (because it implements `EventSubscriberInterface`), and the `ProductCountRoute` dependency is injected via autowire ### Displaying the data in the Storefront diff --git a/guides/plugins/plugins/storefront/add-dynamic-content-via-ajax-calls.md b/guides/plugins/plugins/storefront/add-dynamic-content-via-ajax-calls.md index 6a85b7eb90..78fffd33ec 100644 --- a/guides/plugins/plugins/storefront/add-dynamic-content-via-ajax-calls.md +++ b/guides/plugins/plugins/storefront/add-dynamic-content-via-ajax-calls.md @@ -54,27 +54,10 @@ As you might have seen, this controller isn't too different from the controller The route attribute has an added `defaults: ['XmlHttpRequest' => true]` to allow XmlHttpRequest, and it returns a `JsonResponse` instead of a normal `Response`. Using a `JsonResponse` instead of a normal `Response` causes the data structures passed to it to be automatically turned into a `JSON` string. -The following `services.xml` and `routes.xml` are identical as in the before mentioned article, but here they are for reference anyway: +The controller is automatically registered via autowiring. The `routes.xml` is identical as in the before mentioned article, but here it is for reference anyway: ::: code-group -```xml [PLUGIN_ROOT/src/Resources/config/services.xml] - - - - - - - - - - - - -``` - ```xml [PLUGIN_ROOT/src/Resources/config/routes.xml] - - ```php +// /src/Subscriber/ThemeVariableSubscriber.php - - - -```xml - - - - - - - - - - -``` - - - +With `autoconfigure` enabled in your `services.php`, the subscriber is automatically registered because it implements `EventSubscriberInterface` — no additional configuration is needed. The `ThemeCompilerEnrichScssVariablesEvent` provides the `addVariable()` method which takes the following parameters: @@ -144,10 +122,8 @@ As you can see in the example, we add an input field of the type colorpicker for In order to be able to read this config, you have to inject the `SystemConfigService` to your subscriber: - - - ```php +// /src/Subscriber/ThemeVariableSubscriber.php - - - -```xml - - - - - - - - - - - - -``` - - - +With `autoconfigure` enabled in your `services.php`, the subscriber is automatically registered because it implements `EventSubscriberInterface` — no additional configuration is needed. The `SystemConfigService` argument is injected automatically via `autowire`. * The `SystemConfigService` provides a `get()` method where you can access the configuration structure in the first parameter with a dot notation syntax like `SwagBasicExample.config.fieldName`. The second parameter is the sales channel `id`. With this `id` the config fields can be accessed for each sales channel. * You can get the sales channel id through the getter `getSalesChannelId()` of the `ThemeCompilerEnrichScssVariablesEvent`.