Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions guides/plugins/plugins/bundle.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Shopware plugins extend Symfony bundles and add:
* Asset building integration
* Administration management

Class hierarchy: Plugin → Shopware\Bundle → Symfony\Bundle
Class hierarchy: `Plugin``Shopware\Bundle``Symfony\Bundle`

## When to use a bundle

Expand Down Expand Up @@ -79,7 +79,7 @@ project-root/
└── .shopware-project.yaml
```

The Bundle is typically placed in a project's `src/` folder, which is the standard location for custom code. You still will need to register the Bundle in the project's `config/bundles.php` file.
The Bundle is typically placed in a project's `src/` folder, which is the standard location for custom code. You will still need to register the Bundle in the project's `config/bundles.php` file.

## Choosing the right bundle class

Expand Down Expand Up @@ -144,7 +144,7 @@ if (Feature::isActive('YOUR_FEATURE_FLAG') && InstalledVersions::isInstalled('ve
You can add services, Twig templates, routes, etc., to your Bundle just as you would to a plugin. Create `Resources/config/services.php` and `Resources/config/routes.php` 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, you only need to implement the `Shopware\Core\Framework\ThemeInterface` interface in your bundle class.
This will automatically register your Bundle as a theme and make it available in the Shopware administration.
This will automatically register your Bundle as a theme and make it available in the Administration.
You can also add a `theme.json` file to define the theme configuration like [described here](../themes/configuration/theme-configuration.md).

## Adding migrations
Expand All @@ -170,7 +170,7 @@ class YourBundleName extends Bundle
}
```

Since Bundles don't have a lifecycle, migrations aren't automatically executed. Execute them manually via the console command:
Since bundles don't have a lifecycle, migrations aren't automatically executed. Execute them manually via the console command:

```bash
bin/console database:migrate <BundleName> --all
Expand All @@ -193,11 +193,11 @@ The Shopware CLI cannot automatically detect bundles. Therefore, bundle assets a
{
"extra": {
"shopware-bundles": {
"src/<BundleName>": {
"name": "<BundleName>",
}
}
}
"src/<BundleName>": {
"name": "<BundleName>"
}
}
}
}
```

Expand All @@ -207,8 +207,8 @@ This will tell Shopware CLI where the Bundle is located and its name.

Now that you know about the differences between a Symfony bundle and a Shopware plugin, review the following guides:

* [Dependency Injection](../plugins/services/dependency-injection.md)
* [Listening to events](../plugins/framework/event/listening-to-events.md)
* [Dependency Injection](services/dependency-injection.md)
* [Listening to events](framework/event/listening-to-events.md)

Also check out these useful videos:

Expand Down
24 changes: 7 additions & 17 deletions guides/plugins/plugins/creating-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,10 @@ nav:

---

## Creating Plugins
# Creating Plugins

This guide walks you through creating and scaffolding a basic Shopware plugin so it can be installed locally on your Shopware 6 instance.

::: info
Refer to this video on [Creating a plugin](https://www.youtube.com/watch?v=_Tkoq5W7woI) that shows how to bootstrap a plugin. Also available on our free online training ["Shopware 6 Backend Development"](https://academy.shopware.com/courses/shopware-6-backend-development-with-jisse-reitsma).
Comment thread
tturkowski marked this conversation as resolved.
:::

## Prerequisites

You'll need:
Expand All @@ -21,8 +17,6 @@ You'll need:
* A running Shopware 6 instance; refer to our [Install Shopware 6](../../installation/index.md) guide
* full file system and command line access

`<shopware project root>/custom/plugins` contains all plugins from the Shopware store. You install and manage these plugins via the Shopware Administration.

## 1. Choose a name

Use **UpperCamelCase**, which means that your plugin name must begin with a capital letter too. Whenever possible, begin it with a company prefix to avoid duplicate names (e.g., `SwagBasicExample`). Choose a name that describes your plugin as succinctly and clearly as possible.
Expand All @@ -33,6 +27,8 @@ A vendor prefix is required if you plan to publish your plugin in the [Shopware

## 2. Generate the plugin structure

Plugins are located in `<shopware project root>/custom/plugins` and managed via the Shopware Administration.

From your Shopware project's root directory, run:

```bash
Expand Down Expand Up @@ -93,7 +89,7 @@ This file contains basic metadata that Shopware needs to know about your plugin,
* The technical name
* The description
* The author
* The used license
* The license
* The current plugin version
* The required dependencies
* and other configuration details.
Expand Down Expand Up @@ -157,7 +153,7 @@ Here's an example `composer.json` you can refer to:
If you change the `autoload.psr-4` path (for example, not using `src/`), adjust your directory structure accordingly.
:::

::: Info
::: info
Set up [CI](../../development/testing/ci.md) early. Run static analysis, tests, and `shopware-cli extension build` in CI so your plugin ZIP is reproducible and safe to promote across environments.
:::

Expand All @@ -171,7 +167,7 @@ composer config repositories.shopware composer https://packages.shopware.com

Authentication via API token is required. Refer to ["Using Composer for plugin installation in Shopware"](https://www.shopware.com/en/news/using-composer-for-plugin-installation-in-shopware/) for detailed information.

### Manual creation (optional)
## Manual creation (optional)

In most cases, you should use `bin/console plugin:create`. Manual creation is only useful if you need full control over the structure or are working in a custom setup.

Expand All @@ -190,7 +186,7 @@ SwagBasicExample/
└── SwagBasicExample.php
```

* **namespace**: here, it's `Swag\BasicExample`. We recommend using a combination of your manufacturer prefix and the technical name to name it.
* **Namespace**: here, it's `Swag\BasicExample`. We recommend using a combination of your manufacturer prefix and the technical name to name it.
* **`src/` directory**: recommended but not strictly required.
* **PHP class**: `SwagBasicExample.php`, which you name after your plugin.

Expand All @@ -209,12 +205,6 @@ class SwagBasicExample extends Plugin
}
```

::: warning
If you change the `autoload.psr-4` path (for example, not using `src/`), your directory structure must match that configuration.
:::

And that's it. The basic structure and all necessary files for your plugin to be installable are done.

## Next steps

[Install and activate](./install-activate-plugin.md) your plugin.
12 changes: 5 additions & 7 deletions guides/plugins/plugins/database/custom-fields-of-type-media.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ nav:

# Using Custom Fields of Type Media

## Overview

After adding a custom field of type media in the Administration or via a plugin, you can assign media objects to different entities.
This is often used on products to add additional images to the product detail page.
If you want to learn more about custom fields, take a look at [Adding custom fields](../framework/custom-field/add-custom-field.md).
For more on custom fields, see [Adding custom fields](../framework/custom-field/add-custom-field.md).

## Resolve media custom fields in Twig

Expand All @@ -21,10 +19,10 @@ In the product detail page template, `page.product.translated.customFields.xxx`
public function searchMedia(array $ids, Context $context): MediaCollection { ... }
```

This function resolves the corresponding media entities for the given IDs. Here is an example with a custom field \(`custom_sports_media_id`\) on the product detail page:
This function resolves the corresponding media entities for the given IDs. Here is an example with a custom field (`custom_sports_media_id`) on the product detail page:

```twig
// <plugin root>/src/Resources/views/storefront/page/content/product-detail.html.twig
{# <plugin root>/src/Resources/views/storefront/page/content/product-detail.html.twig #}
{% sw_extends '@Storefront/storefront/page/product-detail/index.html.twig' %}

{% block page_product_detail_media %}
Expand All @@ -48,10 +46,10 @@ After loading the entity, you can use fields like `sportsMedia.url`, `sportsMedi
This function performs a database query on every invocation and should therefore not be used inside a loop.
To resolve multiple IDs at once, pass them as one array.

To read the media objects within the product listing, we recommend the following procedure:
To read the media objects within the product listing, use the following approach:

```twig
// <plugin root>/src/Resources/views/storefront/component/product/listing.html.twig
{# <plugin root>/src/Resources/views/storefront/component/product/listing.html.twig #}
{% sw_extends '@Storefront/storefront/component/product/listing.html.twig' %}

{% block element_product_listing_col %}
Expand Down
20 changes: 8 additions & 12 deletions guides/plugins/plugins/database/database-migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ nav:

# Database Migrations

## Overview

This guide covers what migrations are and how to use them.

Migrations are PHP classes used to manage incremental and reversible database schema changes. Shopware comes with a pre-built Migration System, to take away most of the work for you. Throughout this guide, you will find the `$` symbol representing your command line.

## Prerequisites
Expand All @@ -34,7 +30,7 @@ By default, Shopware 6 is looking for migration files in a directory called `Mig
└── SwagBasicExample.php
```

As you can see there is one file in the `<plugin root>/src/Migration` directory. Below you find a break down of what each part of its name means.
As you can see there is one file in the `<plugin root>/src/Migration` directory. Below you'll find a breakdown of what each part of its name means.

| File Name Snippet | Meaning |
|:-------------------|:------------------------------------------------|
Expand Down Expand Up @@ -106,11 +102,11 @@ class Migration1611740369ExampleDescription extends MigrationStep

As you can see, your migration contains three methods:

* getCreationTimestamp\(\)
* update\(\)
* updateDestructive\(\)
* `getCreationTimestamp()`
* `update()`
* `updateDestructive()`

There is no need to change `getCreationTimestamp()`, it returns the timestamp that's also part of the file name. In the `update()` method you implement non-destructive changes which should always be **reversible**. The `updateDestructive()` method is the follow up step, that is run after `update()` and used for **destructive none reversible changes**, like dropping columns or tables. Destructive migrations are only executed explicitly.
There is no need to change `getCreationTimestamp()`, it returns the timestamp that's also part of the file name. In the `update()` method you implement non-destructive changes which should always be **reversible**. The `updateDestructive()` method is the follow-up step, that is run after `update()` and used for **destructive non-reversible changes**, like dropping columns or tables. Destructive migrations are only executed explicitly.

::: info
You do not add instructions to revert your migrations within the migration class itself. `updateDestructive` is not meant to revert instructions in `update`. Reverting changes in the database is done explicitly in plugin lifecycle method `uninstall`, as explained in the [Plugin Lifecycle guide](../plugin-fundamentals/plugin-lifecycle.md#uninstall).
Expand Down Expand Up @@ -169,7 +165,7 @@ This command will generate a new migration file including the `CREATE TABLE` or
| Option | Meaning |
|:-----------|:---------------------------------------------------------------------------------------------------------------------|
| --bundle | The name of the plugin, when not provided the command will generate a migration in the core |
| --entities | Comma-seperated list of the entities it should create migrations for, it will generate one migration file per entity |
| --entities | Comma-separated list of the entities it should create migrations for, it will generate one migration file per entity |

_Note: Your plugin has to be activated, otherwise your custom entity definition cannot be found._

Expand All @@ -194,7 +190,7 @@ $ ./bin/console database:migrate SwagBasicExample --all

## Advanced migration control

Once you have become familiar with the migration process and the development flow, you may want to have finer control over the migrations performed during the installation and update. In this case the `MigrationCollection` which is only filled with your specific migrations, can be accessed via the `InstallContext` and all its subclasses \(UpdateContext, ActivateContext, ...\). A plugin must reject the automatic execution of migrations in order to have control over the migrations that are executed.
Once you have become familiar with the migration process and the development flow, you may want to have finer control over the migrations performed during the installation and update. In this case the `MigrationCollection` which is only filled with your specific migrations, can be accessed via the `InstallContext` and all its subclasses (`UpdateContext`, `ActivateContext`, ...). A plugin must reject the automatic execution of migrations in order to have control over the migrations that are executed.

Therefore, a typical update method might look more like this:

Expand All @@ -213,4 +209,4 @@ Therefore, a typical update method might look more like this:
}
```

If you don't use the Shopware migration system, an empty collection \(NullObject\) will be in the context.
If you don't use the Shopware migration system, an empty collection (`NullObject`) will be in the context.
4 changes: 1 addition & 3 deletions guides/plugins/plugins/database/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ This section covers:
* How to generate migrations from entity definitions
* How to work with custom fields (e.g., media custom fields)

## Guides
Use migrations for schema changes and structural updates. Use custom fields to extend existing entities without modifying the core schema.

* <PageRef page="./database-migrations" />
* <PageRef page="./custom-fields-of-type-media" />

Use migrations for schema changes and structural updates. Use custom fields to extend existing entities without modifying the core schema.
14 changes: 8 additions & 6 deletions guides/plugins/plugins/in-app-purchases.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# In-App Purchases

::: info
In-App Purchase is available since Shopware version 6.6.9.0
In-App Purchases are available since Shopware version 6.6.9.0
:::

In-App Purchases are a way to lock certain features behind a paywall within the same extension.
Expand All @@ -20,9 +20,9 @@

## Allow users to buy an In-App Purchase

In order to enable others to purchase your In-App Purchase, you must request a checkout for it via the `inAppPurchaseCheckout` store in the administration.
In order to enable others to purchase your In-App Purchase, you must request a checkout for it via the `inAppPurchaseCheckout` store in the Administration.
The checkout process itself is provided by Shopware.
As this is purely functional, it is your responsibility to provide a button and hide it if the IAP cannot be purchased more than once.
It is your responsibility to provide a button and hide it if the IAP cannot be purchased more than once.

```ts
{
Expand Down Expand Up @@ -69,13 +69,15 @@
If you want to check an in-app purchase in the administration:

```js
if (Shopware.InAppPurchase.isActive('MyExtensionName', 'my-iap-identifier')) {};
if (Shopware.InAppPurchase.isActive('MyExtensionName', 'my-iap-identifier')) {

Check warning on line 72 in guides/plugins/plugins/in-app-purchases.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] guides/plugins/plugins/in-app-purchases.md#L72

Add a space between sentences. (SENTENCE_WHITESPACE) Suggestions: ` InAppPurchase` Rule: https://community.languagetool.org/rule/show/SENTENCE_WHITESPACE?lang=en-US Category: TYPOGRAPHY
Raw output
guides/plugins/plugins/in-app-purchases.md:72:13: Add a space between sentences. (SENTENCE_WHITESPACE)
 Suggestions: ` InAppPurchase`
 Rule: https://community.languagetool.org/rule/show/SENTENCE_WHITESPACE?lang=en-US
 Category: TYPOGRAPHY
// ...
}
```

## Event
## Listen to In-App Purchase events

Apps are also able to manipulate the available In-App Purchases as described in
<PageRef page="../apps/gateways/in-app-purchase/in-app-purchase-gateway" title="In App purchase gateway" />
<PageRef page="../apps/gateways/in-app-purchase/in-app-purchase-gateway" title="In-App purchase gateway" />.

Check warning on line 80 in guides/plugins/plugins/in-app-purchases.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] guides/plugins/plugins/in-app-purchases.md#L80

Unpaired symbol: ‘"’ seems to be missing (EN_UNPAIRED_QUOTES) URL: https://languagetool.org/insights/post/punctuation-guide/#what-are-parentheses Rule: https://community.languagetool.org/rule/show/EN_UNPAIRED_QUOTES?lang=en-US Category: PUNCTUATION
Raw output
guides/plugins/plugins/in-app-purchases.md:80:14: Unpaired symbol: ‘"’ seems to be missing (EN_UNPAIRED_QUOTES)
 URL: https://languagetool.org/insights/post/punctuation-guide/#what-are-parentheses 
 Rule: https://community.languagetool.org/rule/show/EN_UNPAIRED_QUOTES?lang=en-US
 Category: PUNCTUATION

Check warning on line 80 in guides/plugins/plugins/in-app-purchases.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] guides/plugins/plugins/in-app-purchases.md#L80

Unpaired symbol: ‘"’ seems to be missing (EN_UNPAIRED_QUOTES) URL: https://languagetool.org/insights/post/punctuation-guide/#what-are-parentheses Rule: https://community.languagetool.org/rule/show/EN_UNPAIRED_QUOTES?lang=en-US Category: PUNCTUATION
Raw output
guides/plugins/plugins/in-app-purchases.md:80:71: Unpaired symbol: ‘"’ seems to be missing (EN_UNPAIRED_QUOTES)
 URL: https://languagetool.org/insights/post/punctuation-guide/#what-are-parentheses 
 Rule: https://community.languagetool.org/rule/show/EN_UNPAIRED_QUOTES?lang=en-US
 Category: PUNCTUATION

Plugins can listen to the `Shopware\Core\Framework\App\InAppPurchases\Event\InAppPurchasesGatewayEvent`.
This event is dispatched after the In-App Purchases Gateway has received the app server response from a gateway
Expand Down
6 changes: 3 additions & 3 deletions guides/plugins/plugins/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Plugins run directly inside the Shopware environment and provide full access to:

Technically, plugins are extensions of [Symfony bundles](./bundle.md). They follow a defined directory structure and, when used as managed extensions, provide a lifecycle (install, update, deactivate, uninstall).

Plugins can ship their own assets, controllers, services, and tests, enabling deep platform and full extensibility across core and custom functionality.
Plugins can ship their own assets, controllers, services, and tests, enabling deep extensibility across core and custom functionality.

## When to create and use a plugin

Expand Down Expand Up @@ -99,7 +99,7 @@ Managed plugins are commonly used for marketplace-distributed extensions. They a

### Bundles

Symfony-based [bundles](../plugins/bundle.md) are installed via Composer. They do not have a Shopware plugin lifecycle and are not managed via the Administration.
Symfony-based [bundles](./bundle.md) are installed via Composer. They do not have a Shopware plugin lifecycle and are not managed via the Administration.

Bundles are useful when you want:

Expand All @@ -119,7 +119,7 @@ For custom projects, it is often preferable to:
* Share one CI pipeline and one set of static analysis rules
* Organize functionality through clean internal directory structure

It does not matter whether static plugins or Symfony bundles internally are used, as much as having:
It does not matter whether static plugins or Symfony bundles are used internally, as much as having:

* Clear domain boundaries
* Consistent structure
Expand Down
12 changes: 8 additions & 4 deletions guides/plugins/plugins/install-activate-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ From your Shopware project root directory, refresh the list of plugins:
bin/console plugin:refresh
```

A warning about the `version` field of the `composer.json` file might appear; this can be ignored. You should see a list like this:
You should see a list like this:

```bash
Shopware Plugin Service
Expand All @@ -28,15 +28,19 @@ Shopware Plugin Service
------------------------------ -------------------------------------------- ----------- ----------------- ---------------------------- ----------- -------- -------------
```

This output is a **good sign**, because this means Shopware recognized your plugin successfully.
This output confirms the plugin was loaded correctly.

::: info
If a warning about the `version` field in `composer.json` appears, it is expected and does not affect the result.
:::

Now install and activate:

```bash
bin/console plugin:install --activate SwagBasicExample
```

This should print the following output:
This prints the following output:

```bash
Shopware Plugin Lifecycle Service
Expand All @@ -48,7 +52,7 @@ Shopware Plugin Lifecycle Service
Plugin "SwagBasicExample" has been installed and activated successfully.
```

If successful, your plugin is now active and ready to use!
Your plugin is now installed and active.

## Next steps

Expand Down
Loading
Loading