- create theme (theme = plugin)
- extend and override templates (twig)
- twig block system (Twig PHP Template Engine from Symfony)
- SASS (SCSS -> CSS) (Syntactically Awesome Stylesheets is a CSS Preprozessor)
- Shopware 6 PluginManager (JS)
- extend and override JS Plugins
- developer.shopware.com/docs | add JS
- Responsive JS
- Bootstrap 4 (CSS, JS) (UI Library with CSS and JS based on jquery slim)
- ES 6
- jquery (slim)
- Webpack (modul bundler, also transform, preprocess CSS/SASS/SCSS etc., bundle JS, CSS, minify CSS and HTML and much more)
- Creating their own custom Shopware themes
- The inheritance and block system
- Using Twig and SASS in Shopware
- Handling CSS and HTML
- Working with JavaScript in Shopware 6 storefront
- Using JavaScript Plugin Base and Plugin Manager
- Debugging JavaScript plugins
- Extending and overwriting of existing JavaScript plugins
- Responsive JavaScript, executing Code for different viewports
- Shopware 6 Academy EN: | Creating and activating a Theme
- Shopware 6 Academy EN: JS Project javascript plugin quantity field
on your local maschine (php, node, npm required)
./psh.phar // press Enter and you get a list of commands
./psh.phar docker:start
./psh.phar docker:ssh // to connect with Docker VM
on virtual Maschine (Docker)
./psh.phar
./psh.phar storefront:dev
./psh.phar storefront:hot-proxy
./bin/console // press Enter and you get a list of commands
./bin/console cache:clear
./bin/console theme:create MyTheme // Replace MyTheme with your custom ThemeName
// OR use the wizard
./bin/console theme:create // press enter and follow instruction
./bin/console plugin:refresh
./bin/console plugin:list
./bin/console plugin:install MyTheme
./bin/console plugin:activate MyTheme
./bin/console theme:change // just follow wizard
import StickyHeader from "./scripts/my-custom-script";
window.PluginManager.register('MyCustomScript', MyCustomScript, '[data-my-script]')
import MyCustomScript from "./scripts/my-custom-script";
window.PluginManager.override('AddToCart', MyCustomScript, '[data-add-to-cart]')
import Plugin from 'src/plugin-system/plugin.class';
export default class MyCustomScript extends Plugin {
init() {
this.PluginManager = window.PluginManager
super.init()
}
}
Important: plugin and SCSS order is important, so which is loaded first!
{
"name": "SwagThemeAdvanced",
"author": "Shopware AG",
"version": 1,
"views": [
"@Storefront",
"@Plugins",
"@SwagThemeAdvanced"
],
"style": [
"app/storefront/src/scss/overrides.scss",
"@Storefront",
"app/storefront/src/scss/base.scss"
],
"script": [
"@Storefront",
"app/storefront/dist/storefront/js/swag-theme-advanced.js"
],
"asset": [
"@Storefront",
"app/storefront/src/assets"
],
"config": {
"blocks": {
"jsPlugins": {
"label": {
"en-GB": "JS Plugin",
"de-DE": "JS Plugin"
}
},
"settings": {
"label": {
"en-GB": "Color Settings",
"de-DE": "Farb-Einstellungen"
}
}
},
"sections": {
"overriddenVariables": {
"label": {
"en-GB": "overridden Variables",
"de-DE": "überschriebende Variablen"
}
},
"newVariables": {
"label": {
"en-GB": "new Variables",
"de-DE": "Custom Variablen"
}
}
},
"fields": {
"showOnScrollPosition": {
"label": {
"en-GB": "Sticky Header Show Position",
"de-DE": "Sticky Header Show Position"
},
"type": "int",
"block": "jsPlugins",
"editable": true,
"value": 500
},
"activateStickyHeader": {
"label": {
"en-GB": "Activate Sticky Header",
"de-DE": "Stick Header aktivieren"
},
"type": "switch",
"custom": {
"bordered": true
},
"value": false,
"editable": true,
"block": "jsPlugins"
},
"sw-color-brand-primary": {
"label": {
"en-GB": "Primary Color",
"de-DE": "Hauptfarbe"
},
"type": "color",
"block": "settings",
"section": "overriddenVariables",
"value": "#48AB18"
},
"cardColor":{
"label": {
"en-GB": "Card Color",
"de-DE": "Kartenfarbe"
},
"type": "color",
"block": "settings",
"section": "newVariables",
"value": "#EF83",
"editable": true
}
}
}
}
{% extends '@Storefront/storefront/layout/navigation/navigation.html.twig' %}
{% block layout_main_navigation %}
{% if theme_config('activateStickyHeader') %}
{#
Does NOT work anymore:
shopware.theme.showOnScrollPosition
https://academy.shopware.com/courses/take/shopware-6-advanced-template-training-english/lessons/9747217-plugin-sticky-header
46:12
#}
<div class="js-navigation-wrapper test"
data-sticky-header="true"
data-sticky-header-options='{
"showOnScrollPosition": "{{ theme_config('showOnScrollPosition') }}"
}'>
{{ parent() }}
</div>
{% else %}
{{ parent() }}
{% endif %}
{% endblock %}
{{ dump() }}
{% block layout_main_navigation %}
{{ parent() }}
{% endblock %}
{% sw_extends '@Storefront/storefront/layout/header/header.html.twig' %}
{% block layout_header_actions_account %}
<a href="{{ path('frontend.account.payment.page') }}" class="btn header-action-btn">
{% sw_icon 'money-card' %}
</a>
{% endblock %}
{{ "myTheme.mySnippetName" | trans( {"%name%": product.name} ) }}
- en-GB.json
- de-DE.json
{
"myTheme": {
"mySnippetName" : "Show Details",
"mySnippetName2" : "Show Details 2"
}
}
- create example-plugin.plugin.js at plugin-root/src/Resources/app/storefront/src/example-plugin
example-plugin.plugin.js
- ExamplePlugin extends the Plugin Class from Shopware 6
- init() method. This method will be called when your plugin gets initialized and is the entrypoint to your custom logic.
import Plugin from 'src/plugin-system/plugin.class';
export default class ExamplePlugin extends Plugin {
init() {
window.addEventListener('scroll', this.onScroll.bind(this));
}
onScroll() {
if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) {
alert('Seems like there\'s nothing more to see here.');
}
}
}
// Import all necessary Storefront plugins
import ExamplePlugin from './example-plugin/example-plugin.plugin';
// Register your plugin via the existing PluginManager
const PluginManager = window.PluginManager;
PluginManager.register('ExamplePlugin', ExamplePlugin);
import { COOKIE_CONFIGURATION_UPDATE } from 'src/plugin/cookie/cookie-configuration.plugin';
document.$emitter.subscribe(COOKIE_CONFIGURATION_UPDATE, eventCallback);
function eventCallback(updatedCookies) {
if (typeof updatedCookies.detail['cookie-key-1'] !== 'undefined') {
// The cookie with the cookie attribute "cookie-key-1" either is set active or from active to inactive
let cookieActive = updatedCookies.detail['cookie-key-1'];
} else {
// The cookie with the cookie attribute "cookie-key-1" was not updated
}
}
Let's have a look at the code. There's one thing you have to understand first. When a plugin calls this.$emitter.publish, this event is fired on the plugin's own $emitter instance. This means: Every plugin has its own instance of the emitter. Therefore, you cannot just use this.$emitter.subscribe to listen to other plugin's events.
import Plugin from 'src/plugin-system/plugin.class';
export default class EventsPlugin extends Plugin {
init() {
const plugin = window.PluginManager.getPluginInstanceFromElement(document.querySelector('[data-cookie-permission]'), 'CookiePermission');
plugin.$emitter.subscribe('hideCookieBar', this.onHideCookieBar);
}
onHideCookieBar() {
alert("The cookie bar has been hidden!");
}
}
import Plugin from 'src/plugin-system/plugin.class';
export default class ExamplePlugin extends Plugin {
}
import Plugin from 'src/plugin-system/plugin.class';
export default class ExamplePlugin extends Plugin {
init() {
window.addEventListener('scroll', this.onScroll.bind(this));
}
onScroll() {
if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) {
alert('Seems like there\'s nothing more to see here.');
}
}
}
import CookiePermissionPlugin from 'src/plugin/cookie/cookie-permission.plugin';
import CookieStorage from 'src/helper/storage/cookie-storage.helper';
export default class MyCookiePermission extends CookiePermissionPlugin {
init() {
CookieStorage.setItem(this.options.cookieName, '');
super.init();
}
_hideCookieBar() {
if (confirm('Do you want to hide the cookie bar?')) {
super._hideCookieBar();
}
}
}
{% set productSliderOptions = {
productboxMinWidth: sliderConfig.elMinWidth.value ? sliderConfig.elMinWidth.value : '',
slider: {
gutter: 30,
autoplayButtonOutput: false,
nav: false,
mouseDrag: false,
controls: sliderConfig.navigation.value ? true : false,
autoplay: sliderConfig.rotate.value ? true : false
}
} %}
<div data-gb-custom-block data-tag="block">
<div class="base-slider"
data-product-slider="true"
data-product-slider-options="{{ productSliderOptions|default({})|json_encode|escape('html_attr') }}">
</div>
</div>
Finding events So before you can start reacting and listening to events, you need to find them first. Since not every plugin implements events, they can be hard to find by just looking through the code. Instead, rather search for this.$emitter.publish in the directory platform/src/Storefront/Resources/app/storefront/src to find all occurrences of events being published. This way, you may or may not find an event useful for your needs, so you don't have to override other JavaScript plugins.
import Plugin from 'src/plugin-system/plugin.class';
export default class EventsPlugin extends Plugin {
init() {
const plugin = window.PluginManager.getPluginInstanceFromElement(document.querySelector('[data-cookie-permission]'), 'CookiePermission');
plugin.$emitter.subscribe('hideCookieBar', this.onHideCookieBar);
}
onHideCookieBar() {
alert("The cookie bar has been hidden!");
}
}
https://developer.shopware.com/docs/guides/plugins/plugins/storefront/fetching-data-with-javascript
import Plugin from 'src/plugin-system/plugin.class';
import HttpClient from 'src/service/http-client.service';
export default class ExamplePlugin extends Plugin {
init() {
this._client = new HttpClient();
this.fetchData();
}
// ...
fetchData() {
this._client.get('/widgets/checkout/info', this.handleData);
}
handleData(response) {
console.log(response);
}
}