App Scripts allow your app to include logic that is executed inside the Shopware execution stack. It allows you to build richer extensions that integrate more deeply with Shopware.
{% hint style="info" %} Note that app scripts were introduced in Shopware 6.4.8.0, and are not supported in previous versions. {% endhint %}
The entry point for each script are the so-called "Hooks". You can register one or more scripts inside your app, that should be executed whenever a specific hook is triggered. Through the hook your script gets access to the data of the current execution context and can react to or manipulate the data in some way.
See the Hooks reference for a complete list of all available.
At the core App Scripts are twig files that are executed in a sandboxed environment. Based on which hook the script is registered to, the script has access to the data of that hook, and pre-defined services, that can be used to execute your custom logic.
Apps scripts are placed in the Resources/scripts
directory of your app. For each hook you want to execute a script on, create a new subdirectory. The name of the subdirectory needs to match the name of the hook.
You can place one or more .twig
files inside each of these subdirectories, that will be executed when the hook gets triggered.
The file structure of your apps should look like this:
└── DemoApp
├── Resources
│ └── scripts // all scripts are stored in this folder
│ ├── product-page-loaded // each script in this folder will be executed when the `product-page-loaded` hook is triggered
│ │ └── my-first-script.twig
│ ├── cart
│ │ ├── first-cart-script.twig
│ │ └── second-cart-script.twig // you can execute multiple scripts per hook
│ └── ...
└── manifest.xml
Sometimes scripts can become more complex or you want to extract common functionaltiy. Thus it is handy to split your scripts into smaller parts that can later be included in other scripts.
In order to do that you can compose your reusable scripts into twig macros, put them inside a dedicated include
folder and then import them using the twig import functionality.
└── DemoApp
├── Resources
│ └── scripts
│ ├── include
│ │ └── media-repository.twig // this script may be included into the other scripts
│ ├── cart
│ │ ├── first-cart-script.twig
│ └── ...
└── manifest.xml
Note that app scripts can use the return
keyword to return values to the caller.
A basic example may look like this:
{% code title="Resources/scripts/include/media-repository.twig" %} {% raw %}
{% macro getById(mediaId) %}
{% set criteria = {
'ids': [ mediaId ]
} %}
{% return services.repository.search('media', criteria).first %}
{% endmacro %}
{% endraw %} {% endcode %}
{% code title="Resources/scripts/cart/first-cart-script.twig" %} {% raw %}
{% import "include/media-repository.twig" as mediaRepository %}
{% set mediaEntity = mediaRepository.getById(myMediaId) %}
{% endraw %} {% endcode %}
Inside the app script you have access to the storefront translation mechanism, by using the |trans
-filter.
{% raw %}
{% set translated = 'my.snippet.key'|trans %}
{% do call.something('my.snippet.key'|trans) %}
{% endraw %}
In addition to the default twig syntax, app scripts can also use a more PHP-flavoured syntax.
Instead of using the rather verbose {% if var is same as(1) %}
you can use the more dense ===
equality checks.
{% raw %}
{% if var === 1 %}
...
{% endif %}
{% endraw %}
Additionally, you can also use the !==
not equals operator as well.
{% raw %}
{% if var !== 1 %}
...
{% endif %}
{% endraw %}
Instead of the for...in
syntax for loops you can also use a foreach
tag.
{% raw %}
{% foreach list as entry %}
{{ entry }}
{% break %}
{% endforeach %}
{% endraw %}
You can use a is
check to check the type of a variable.
{% raw %}
{% if var is string %}
...
{% endif %}
{% endraw %}
The following types are supported:
true
false
boolean
/bool
string
scalar
object
integer
/int
float
callable
array
You can cast variables into different types with the intval
filter.
{% raw %}
{% if '5'|intval === 5 %}
{# always evaluates to true #}
{% endif %}
{% endraw %}
The following type casts are supported:
intval
strval
boolval
floatval
Instead of using AND
or OR
in if-conditions, you can use the &&
or ||
shorthands.
{% raw %}
{% if condition === true && condition2 === true %}
...
{% endif %}
{% endraw %}
You can use the return
tag to return values from inside macros.
{% raw %}
{% macro foo() %}
{% return 'bar' %}
{% endmacro %}
{% endraw %}
Depending on the hook that triggered the execution of you script you get access to different services, you can use inside your scripts e.g. to access data inside Shopware or to manipulate the cart. Take a look at the hook reference to get a complete list of all available services per hook.
Additionally, we added a ServiceStubs
-class, that can be used as typehint in your script, so you get auto-completion features of your IDE.
{% raw %}
{# @var services \Shopware\Core\Framework\Script\ServiceStubs #}
{% set configValue = services.config.app('my-app-config') %}
{% endraw %}
{% hint style="info" %} The stub class contains all services, but depending on the hook not all of them are available. {% endhint %}
Assuming your app adds a custom field set for the product entity with a custom media entity select field.
When you want to display the file of the media entity in the storefront, it is not easily possible, because in the template's data you only get the id of the media entity, but not the url of the media file itself.
For this case you can add an app script on the product-page-loaded
-hook, that loads the media entity by id and adds it to the page object, so the data is available in templates.
{% code title="Resources/scripts/product-page-loaded/add-custom-media.twig" %} {% raw %}
{# @var services \Shopware\Core\Framework\Script\ServiceStubs #}
{% set page = hook.page %}
{# @var page \Shopware\Storefront\Page\Product\ProductPage #}
{% if page.product.customFields.myCustomMediaField is not defined %}
{% return %}
{% endif %}
{% set criteria = {
'ids': [ page.product.customFields.myCustomMediaField ]
} %}
{% set media = services.repository.search('media', criteria).first %}
{% do page.addExtension('swagMyCustomMediaField', media) %}
{% endraw %} {% endcode %}
For a more detailed example on how to load additional data, please refer to the data loading guide.
Alternatively take a look at the cart manipulation guide to get an in-depth explanation on how to manipulate the cart with scripts.
You can get information about what scripts were triggered on a specific storefront page inside the Symfony debug toolbar.
{% hint style="info" %}
The debug toolbar is only visible if your Shopware installation is in APP_ENV = dev
, please ensure you set the correct env - e.g. in your .env
file - when developing app scripts .
{% endhint %}
You can find all hooks that are triggered and the scripts that are executed for each by clicking on the script
icon.
That will open the Symfony profiler in the script detail view, where you can see all triggered hooks and the count of the scripts that were executed for each script at the top.
Additionally, you can use the debug.dump()
function inside your scripts to dump data to the debug view.
A script like this:
{% raw %}
{% do debug.dump(hook.page) %}
{% endraw %} Will dump the page object to the debug view.