-
\Shopware\Storefront\Controller\StorefrontController::forwardToRoutenow handles parameters correctly. -
Request scope changes during a single request handle are prohibited.
-
Use
\Shopware\Core\Framework\Routing\RequestTransformerInterface::extractInheritableAttributesif you want to create a true subrequest. -
The Context will only be resolved when a valid scope is dipatched.
-
All admin and api routes are now authentication protected by default.
-
Changed the
\Symfony\Component\HttpKernel\KernelEvents::CONTROLLEREvent-Subscriber priorities. Now all Shopware Listeners are handled after the core symfony event handlers. You can find the priorities in\Shopware\Core\Framework\Routing\KernelListenerPriorities. -
Removed the
Shopware\Core\Framework\Routing\Event\RouteScopeWhitlistCollectEventin favor of a taggable interface namedShopware\Core\Framework\Routing\RouteScopeWhitelistInterface. -
Requests can no longer be forwarded across different request scopes.
-
If you have implemented a custom FieldResolver, you need to implement the
getJoinBuildermethod. -
\Shopware\Core\Framework\DataAbstractionLayer\Search\Criteriaassociation handlingWe removed the
$criteriaparameter from theaddAssociationfunction. By setting the criteria object the already added criteria was overwritten. This led to problems especially with multiple extensions by plugins. Furthermore the functionaddAssociationPathwas removed from the criteria. The following functions are now available on the criteria object:-
addAssociation(string $path): selfThis function allows you to load additional associations. The transferred path can also point to deeper levels:
$criteria->addAssociation('categories.media.thumbnails);For each association in the provided path, a criteria object with the corresponding association is now ensured. If a criteria is already stored, it will no longer be overwritten.
-
getAssociation(string $path): CriteriaThis function allows access to the criteria for an association. If the association is not added to the criteria, it will be created automatically. The provided path can also point to deeper levels:
$criteria = new Criteria(); $thumbnailCriteria = $criteria->getAssociation('categories.media.thumbnail'); $thumbnailCriteria->setLimit(5);
-
-
Added RouteScopes as required Annotation for all Routes
We have added Scopes for Routes. The Scopes hold and resolve information of allowed paths and contexts. A RouteScope is mandatory for a Route. From now on every Route defined, needs a defined RouteScope.
RouteScopes are defined via Annotation:
/** * @RouteScope(scopes={"storefront"}) * @Route("/account/login", name="frontend.account.login.page", methods={"GET"}) */ /** * @RouteScope(scopes={"storefront", "my_additional_scope"}) * @Route("/account/login", name="frontend.account.login.page", methods={"GET"}) */
-
If you have implemented a custom
\Shopware\Core\Framework\DataAbstractionLayer\Indexing\IndexerInterface, you need to implement thepartialmethod. Here are two good example implementations:- simple iteration:
\Shopware\Core\Content\Product\DataAbstractionLayer\Indexing\ProductCategoryTreeIndexer::partial - iteration with several ids:
\Shopware\Core\Content\Category\DataAbstractionLayer\Indexing\BreadcrumbIndexer::partial
- simple iteration:
-
If you have implemented the
\Shopware\Core\Framework\DataAbstractionLayer\Search\EntityAggregatorInterface, you need to return now a\Shopware\Core\Framework\DataAbstractionLayer\Search\AggregationResult\AggregationResultCollection -
We changed the constructor parameter order of
\Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Aggregation -
Aggregations are now returned directly in a collection:
$criteria->addAggregation( new SumAggregation('sum-price', 'product.price') ); $result = $this->repository->search($criteria, $context); /** @var SumResult $sum */ $sum = $result->getAggregations()->get('sum-price'); $sum->getSum();
-
ValueCountAggregationandValueAggregationremoved, useTermsAggregationinstead.$criteria->addAggregation( new TermsAggregation('category-ids', 'product.categories.id') ); $result = $this->repository->aggregate($criteria, $context); /** @var TermsResult $categoryAgg */ $categoryAgg = $result->get('category-ids'); foreach ($categoryAgg->getBuckets() as $bucket) { $categoryId = $bucket->getKey(); $count = $bucket->getCount(); }
-
We changed the type hint of
\Shopware\Core\Framework\DataAbstractionLayer\Event\EntityAggregationResultLoadedEvent::getResulttoAggregationResultCollection -
We removed the
Aggregation::groupByFieldsandAggregation::filtersproperty, use\Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\FilterAggregationand\Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\TermsAggregationinstead$criteria->addAggregation( new FilterAggregation( 'filter', new TermsAggregation('category-ids', 'product.categories.id'), [new EqualsAnyFilter('product.active', true)] ) );
-
We removed the default api limit for association. Associations are no longer paginated by default. In order to load the association paginated, the limit and page parameter can be sent along:
{ "associations": { "categories": { "page": 2, "limit": 5 } } } -
We've changed the kernel plugin loading. Replace the
ClassLoaderwith an instance of\Shopware\Core\Framework\Plugin\KernelPluginLoader\KernelPluginLoader.Before:
$kernel = new \Shopware\Core\Kernel($env, $debug, $classLoader, $version);
After:
$connection = \Shopware\Core\Kernel::getConnection(); $pluginLoader = new \Shopware\Core\Framework\Plugin\KernelPluginLoader\DbalKernelPluginLoader($classLoader, null, $connection); $kernel = new \Shopware\Core\Kernel($env, $debug, $pluginLoader, $version); // or without plugins $pluginLoader = new \Shopware\Core\Framework\Plugin\KernelPluginLoader\StaticKernelPluginLoader($classLoader, null, []); $kernel = new \Shopware\Core\Kernel($env, $debug, $pluginLoader, $version); // or with a static plugin list $plugins = [ [ 'baseClass' => 'SwagTest\\SwagTest', 'active' => true, 'path' => 'platform/src/Core/Framework/Test/Plugin/_fixture/plugins/SwagTest', 'autoload' => ['psr-4' => ['SwagTest\\' => 'src/']], 'managedByComposer' => false, ] ]; $pluginLoader = new \Shopware\Core\Framework\Plugin\KernelPluginLoader\StaticKernelPluginLoader($classLoader, null, $plugins); $kernel = new \Shopware\Core\Kernel($env, $debug, $pluginLoader, $version);
-
the parameter for the
\Shopware\Core\Kernel::bootmethod was removed. Instead, use theStaticKernelPluginLoaderwith an empty list. -
If you have implemented a custom
Shopware\Core\Framework\DataAbstractionLayer\FieldSerializer\AbstractFieldSerializer, you must now provide aDefinitionInstanceRegistrywhen calling the super constructor -
Removed
Shopware\Core\Framework\DataAbstractionLayer\EntityWrittenContainerEvent::getEventByDefinition. UsegetEventByEntityNameinstead, which takes the entity name instead of the entity classname but proved the same functionality. -
Removed
getDefinitionand the correspondingdefinitionmember from\Shopware\Core\Framework\DataAbstractionLayer\EntityWriteResultsand...\Event\EntityWrittenEvent. Classes which used this function can access the name of the written entity via the new methodgetEntityNameand retrieve the definition using theDefinitionInstanceRegistry -
Replace service id
shopware.cachewithcache.object -
If you invalidated the entity cache over the
shopware.cacheservice, use the\Shopware\Core\Framework\Adapter\Cache\CacheClearerinstead. -
All customer events in
Shopware\Core\Checkout\Customer\Eventnow get theShopware\Core\Syste\SalesChannel\SalesChannelContextinstead ofShopware\Core\Framework\Contextand asalesChannelId -
Implement
getNamefor classes that implement\Shopware\Core\Framework\DataAbstractionLayer\Indexing\IndexerInterface -
We've moved the seo module into the core. Replace the namespace
Shopware\Storefront\Framework\Seo\withShopware\Core\Content\Seo\ -
Switch the usage of
\Shopware\Core\Framework\Migration\MigrationStep::addForwardTrigger()and\Shopware\Core\Framework\Migration\MigrationStep::addBackwardTrigger(), as the execution conditions were switched. -
\Shopware\Core\Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryEntity::$trackingCodehas been replaced with\Shopware\Core\Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryEntity::$trackingCodes. -
Add Bearer Auth Token to requests to
/api/v{version}/_info/entity-schema.jsonand/api/v{version}/_info/business-events.jsonroutes -
Removed
shopware.api.api_browser.publicconfig value, useshopware.api.api_browser.auth_required = trueinstead, to limit access to the open api routes -
Replace
product/category.extensions.seoUrlswithproduct/category.seoUrls -
Dropped
additionalTextcolumn of product entity, usemetaDescriptioninstead -
If your entity definition overwrites the
\Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition::getDefaultsmethod, you will have to remove the parameter, as it is not needed anymore. Remove the check$existence->exists()as this is done before by the Core now. If you want to define different defaults for child entities, overwrite\Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition::getChildDefaults -
If you depend on
\Shopware\Core\Framework\Context::createDefaultContext()outside of tests, pass the context as a parameter to your method instead -
The Shopware entity cache has been removed and has been replaced by a symfony cache pool. You have to remove any configuration files pointing to
shopware.entity_cache.Example: Redis implementation
framework: cache: app: cache.adapter.redis default_redis_provider: 'redis://redis.server'
Example: Disable the object cache only
framework: cache: pools: cache.object: adapter: cache.adapter.array
Example: Disable the cache entirely
framework: cache: app: cache.adapter.array
-
Add the
extractInheritableAttributes()function to your implementations of\Shopware\Core\Framework\Routing\RequestTransformerInterface -
Find and replace
Shopware\Core\Framework\AclwithShopware\Core\Framework\Api\Acl -
Find and replace
Shopware\Core\Framework\CustomFieldwithShopware\Core\System\CustomField -
Find and replace
Shopware\Core\Framework\LanguagewithShopware\Core\System\Language -
Find and replace
Shopware\Core\Framework\SnippetwithShopware\Core\System\Snippet -
Find and replace
Shopware\Core\Framework\DoctrinewithShopware\Core\Framework\DataAbstractionLayer\Doctrine -
Find and replace
Shopware\Core\Framework\PricingwithShopware\Core\Framework\DataAbstractionLayer\Pricing -
Find and replace
Shopware\Core\Framework\VersionwithShopware\Core\Framework\DataAbstractionLayer\Version -
Find and replace
Shopware\Core\Framework\FakerwithShopware\Core\Framework\Demodata\Faker -
Find and replace
Shopware\Core\Framework\PersonalDatawithShopware\Core\Framework\Demodata\PersonalData -
Find and replace
Shopware\Core\Framework\LoggingwithShopware\Core\Framework\Log -
Find and replace
Shopware\Core\Framework\ScheduledTaskwithShopware\Core\Framework\MessageQueue\ScheduledTask -
Find and replace
Shopware\Core\Framework\TwigwithShopware\Core\Framework\Adapter\Twig -
Find and replace
Shopware\Core\Framework\AssetwithShopware\Core\Framework\Adapter\Asset -
Find and replace
Shopware\Core\Framework\ConsolewithShopware\Core\Framework\Adapter\Console -
Find and replace
Shopware\Core\Framework\CachewithShopware\Core\Framework\Adapter\Cache -
Find and replace
Shopware\Core\Framework\FilesystemwithShopware\Core\Framework\Adapter\Filesystem -
Find and replace
Shopware\Core\Framework\TranslationwithShopware\Core\Framework\Adapter\Translation -
Find and replace
Shopware\Core\Framework\SeowithShopware\Core\Content\Seo -
Find and replace
Shopware\Core\Content\DeliveryTimewithShopware\Core\System\DeliveryTime -
Find and replace
Shopware\Core\Framework\Context\withShopware\Core\Framework\Api\Context\ -
Find and replace
Shopware\Core\System\User\Service\UserProvisionerwithShopware\Core\System\User\Service\UserProvisioner- Warning: Do not replace
Shopware\Core\Framework\ContextwithShopware\Core\Framework\Api\Context, this would replace theFramework\Context.phpusage.
- Warning: Do not replace
-
Added unique constraint for
iso_codecolumn ofcurrencytable. The migration can fail if there are already duplicateiso_codesin the table -
Replace
mailerusage withcore_mailerin your service definitions. -
If you call
\Shopware\Core\Framework\Api\Response\ResponseFactoryInterface::createDetailResponseor\Shopware\Core\Framework\Api\Response\ResponseFactoryInterface::createListingResponsein your plugin, the first parameter to be passed now is theCriteriaobject with which the data was loaded. -
We changed the type hint of
Shopware\Core\Framework\Validation\ValidationServiceInterface::buildCreateValidationandShopware\Core\Framework\Validation\ValidationServiceInterface::buildUpdateValidationtoSalesChannelContext -
Replace
\Shopware\Core\Framework\Plugin::getExtraBundleswith\Shopware\Core\Framework\Plugin::getAdditionalBundles. Dont use both. -
We implemented the new
Shopware\Core\HttpKernelclass which simplifies the kernel initialisation. This kernel can simply initialed and can be used in yourindex.phpfile as follow:$request = Request::createFromGlobals(); $kernel = new HttpKernel($appEnv, $debug, $classLoader); $result = $kernel->handle($request); $result->getResponse()->send(); $kernel->terminate($result->getRequest(), $result->getResponse());
-
If you used the
\Shopware\Core\Content\Seo\SeoUrlGeneratorin your sources, please use thegeneratefunction instead of thegenerateSeoUrls -
If you update your decoration implementations of
\Shopware\Core\Framework\Validation\ValidationServiceInterfaceto\Shopware\Core\Framework\Validation\DataValidationFactoryInterfacemake sure to still implement the old interface and when calling the inner implementation please make sure to check if the inner implementation already supports the interface, likepublic function createValidation(SalesChannelContext $context): DataValidationDefinition { if ($this->inner instanceof DataValidationFactoryInterface) { $validation = $this->inner->create($context); } else { $validation = $this->inner->buildCreateValidation($context->getContext()); } $this->modifyValidation($validation); return $validation; }
-
We will change the
\Shopware\Core\Framework\DataAbstractionLayer\Cache\EntityCacheKeyGenerator::getEntityTagsignature from- Before:
public function getEntityTag(string $id, EntityDefinition $definition): string - After:
public function getEntityTag(string $id, string $entityName): string - If you called this function, simply replace the second function parameter to your entity name
- Currently both ways are supported. The
string $entityNametype hint will be added withv6.3.0
- Before:
-
We'll stop deleting the plugin migration data on plugin uninstall in
v6.3.0If you want to keep removing it anyway you have to call\Shopware\Core\Framework\Plugin::removeMigrations.
- The admin core framework of shopware from
src/core/should always be accessed via the global availableShopwareobject and not via static imports. This is important to provide a consistent access point to the core framework of the shopware administration, either you are using Webpack or not. It will also ensure the correct bundling of source files via Webpack. Especially third party plugins have to ensure to access the core framework only via the globalShopwareobject. Using the concept of destructuring can help to access just specific parts of the framework and maintain readability of your code. Nevertheless you can use static imports in your plugins to import other source files of your plugin or NPM dependencies.
Before:
import { Component } from 'src/core/shopware';
import Criteria from 'src/core/data-new/criteria.data';
import template from './my-component.html.twig';
Component.register('my-component', {
template,
inject: ['repositoryFactory', 'context'],
data() {
return {
products: null
};
},
computed: {
productRepository() {
return this.repositoryFactory.create('product');
}
},
created() {
this.getProducts();
},
methods: {
getProducts() {
const criteria = new Criteria();
criteria.addAssociation('manufacturer');
criteria.addSorting(Criteria.sort('product.productNumber', 'ASC'));
criteria.limit = 10;
return this.productRepository
.search(criteria, this.context)
.then((result) => {
this.products = result;
});
}
}
});After:
import template from './my-component.html.twig';
const { Component } = Shopware;
const { Criteria } = Shopware.Data;
Component.register('my-component', {
template,
inject: ['repositoryFactory', 'context'],
data() {
return {
products: null
};
},
computed: {
productRepository() {
return this.repositoryFactory.create('product');
}
},
created() {
this.getProducts();
},
methods: {
getProducts() {
const criteria = new Criteria();
criteria.addAssociation('manufacturer');
criteria.addSorting(Criteria.sort('product.productNumber', 'ASC'));
criteria.limit = 10;
return this.productRepository
.search(criteria, this.context)
.then((result) => {
this.products = result;
});
}
}
});
-
Replaced vanilla-colorpicker dependency with custom-build vuejs colorpicker
editorFormatandcolorCallbackgot replaced withcolorOutput- the default value for the property
alphais nowtrue
Before:
<sw-colorpicker value="myColorVariable" editorFormat="rgb" colorCallback="rgbString"> </sw-colorpicker>After:
<sw-colorpicker value="myColorVariable" colorOutput="rgb" :alpha="false"> </sw-colorpicker> -
The Shopping Experiences data handling has changed. To get an entity resolved in an element you now need to configure a configfield like this:
product: {
source: 'static',
value: null,
required: true,
entity: {
name: 'product',
criteria: criteria
}
}Where the criteria is the required criteria for this entity
(in this case const criteria = new Criteria(); criteria.addAssociation('cover');).
Furthermore you can now define your custom collect and enrich method in the cmsService.registerCmsElement method.
See 2019-09-02-cms-remove-store.md for more information
-
Refactored select components and folder structure
- Select components are now located in the folder
administration/src/app/component/form/selectdivided in the subfoldersbaseandentitybasecontains the base components for creating new selects and the staticsw-single-selectandsw-multi-selectentitycontains components working with the api such assw-entity-multi-selectorsw-entity-tag-select
- Components work with v-model and do not mutate the value property anymore
- Components are based on the sw-field base components to provide a consistent styling, error handling etc for all form fields
- Select components are now located in the folder
-
Important Change: Removed module export of
Shopwareand all childrenBefore:
import Application from 'src/core/shopware';
After:
const Application = Shopware.Application;
-
Important Change:
contextis now only available inserviceBefore:
const context = Shopware.Application.getContainer('init').contextService;After:
const context = Shopware.Application.getContainer('service').context; -
Important Change: You can use specific helper functions for components with
getComponentHelper()Before:
import { mapApiErrors } from 'src/app/service/map-errors.service'; import { mapState, mapGetters } from 'vuex';After:
const { mapApiErrors, mapState, mapGetters } = Shopware.Component.getComponentHelper(); -
Important Change: All factories and services are initialized before app starts
Before:
import deDeSnippets from './snippet/de-DE.json'; import enGBSnippets from './snippet/en-GB.json'; Shopware.Application.addInitializerDecorator('locale', (localeFactory) => { localeFactory.extend('de-DE', deDeSnippets); localeFactory.extend('en-GB', enGBSnippets); return localeFactory; });After:
import deDeSnippets from './snippet/de-DE.json'; import enGBSnippets from './snippet/en-GB.json'; Shopware.Locale.extend('de-DE', deDeSnippets); Shopware.Locale.extend('en-GB', enGBSnippets); -
Component have to be registered before they can be used in the modules
Before:
export default { name: 'demo-component', ... }import demoComponent from './page/demo-component'; Module.register('demo-module', { routes: { index: { component: demoComponent, path: 'index', meta: { parentPath: 'sw.demo.index' } } } });After:
Shopware.Component.register('demo-component', { ... });import './page/demo-component'; Module.register('demo-module', { routes: { index: { component: 'demo-component', path: 'index', meta: { parentPath: 'sw.demo.index' } } } }); -
Refactored administration booting process
- Plugins are now injected asynchronous after initialization of all dependencies
- Plugins have full access to all functionalities which are used by the
app - The booting of the login is separated from the application booting. Therefore the login is not expandable with plugins anymore.
-
We unified the implementation of
\Shopware\Core\Framework\DataAbstractionLayer\Search\Criteriaand the Admin criteriasrc/core/data-new/criteria.data.js-
Removed
addAssociationPath -
Changed signature of
addAssociationaddAssociation(path);
-
addAssociationnow only ensures that the association is loaded as well. No Criteria can be passed anymore. The function supports instead the specification of a nested path. The function always returns the criteria it is called on to allow chaining to continue.const criteria = new Criteria(); criteria.addAssociation('product.categories.media') .addAssociation('product.cover') .addAssociation('product.media');
-
The
getAssociationfunction now accepts the passing of a nested path. Furthermore, the function now ensures that a criteria is created for each path segment. Then it returns the criteria for the last path segment.const criteria = new Criteria(); const mediaCriteria = criteria.getAssociation('product.categories.media'); mediaCriteria.addSorting(Criteria.sort('media.fileName', 'ASC')); mediaCriteria.addFilter(Criteria.equals('fileName', 'testImage'));
-
-
Shopping Experience sections.
The Shopping Experiences now have sections to separate the blocks of a page. Also the change allows it to have different types of sections eg. one with a sidebar.
Structure is now Page->Section->blocks->slots
To migrate your existing data runbin/console database:migrate --all Shopware\\
See2019-09-27-breaking-change-cms-sectionsfor more information -
Context is seperated in App and Api Context
Before:
Shopware.Context
After:
Shopware.Context.app // or Shopware.Context.api
Before:
inject: ['context'], ... this.repository.search(criteria, context)
or
inject: ['apiContext'], ... this.repository.search(criteria, apiContext)
After:
Now you do not need to inject the context and can use the context directly.this.repository.search(criteria, Shopware.Context.api)
-
State was replaced by Vuex state. The old state was renamed to
StateDeprecatedBefore:
Shopware.State
After:
Shopware.StateDeprecated
-
Refactored the multiple inheritance of vuejs components and
$supermethod with a breaking change!The syntax for the
$supercall has been changed as follows.this.$super('relatedMethodName', ...args);
You can use
$superfor computed properties and methods to invoke the behaviour of the component you want to extend or override.Before:
import template from './test-component.html.twig'; ComponentFactory.register('test-component', { template, data() { return { _value: ''; } }, computed: { fooBar() { return 'fooBar'; }, currentValue: { get() { return this._value; }, set(value) { this._value = value; } } }, methods: { uppercaseCurrentValue() { return this.currentValue.toUpperCase(); } } }); ComponentFactory.extend('extension-1', 'test-component', { computed: { fooBar() { const prev = this.$super.fooBar(); return `${prev}Baz`; }, currentValue: { get() { this.$super.currentValue(); return `foo${this._value}`; }, set(value) { this.$super.currentValue(value); this._value = `${value}Baz!`; } } }, methods: { uppercaseCurrentValue() { const prev = this.$super.uppercaseCurrentValue(); return prev.reverse(); } } });
After:
import template from './test-component.html.twig'; ComponentFactory.register('test-component', { template, data() { return { _value: ''; } }, computed: { fooBar() { return 'fooBar'; }, currentValue: { get() { return this._value; }, set(value) { this._value = value; } } }, methods: { uppercaseCurrentValue() { return this.currentValue.toUpperCase(); } } }); ComponentFactory.extend('extension-1', 'test-component', { computed: { fooBar() { const prev = this.$super('fooBar'); return `${prev}Baz`; }, currentValue: { get() { this.$super('currentValue.get'); return `foo${this._value}`; }, set(value) { this.$super('currentValue.set', value); this._value = `${value}Baz!`; } } }, methods: { uppercaseCurrentValue() { const prev = this.$super('uppercaseCurrentValue'); return prev.reverse(); } } });
-
Added new properties to the view in the
sw_sales_channel_detail_content_viewblock.Before:
{% block sw_sales_channel_detail_content_view %} <router-view :salesChannel="salesChannel" :customFieldSets="customFieldSets" :isLoading="isLoading" :key="$route.params.id"> </router-view> {% endblock %}After:
{% block sw_sales_channel_detail_content_view %} <router-view :salesChannel="salesChannel" :productExport="productExport" :storefrontSalesChannelCriteria="storefrontSalesChannelCriteria" :customFieldSets="customFieldSets" :isLoading="isLoading" :productComparisonAccessUrl="productComparison.productComparisonAccessUrl" :key="$route.params.id" :templateOptions="productComparison.templateOptions" :showTemplateModal="productComparison.showTemplateModal" :templateName="productComparison.templateName" @template-selected="onTemplateSelected" @access-key-changed="generateAccessUrl" @domain-changed="generateAccessUrl" @invalid-file-name="setInvalidFileName(true)" @valid-file-name="setInvalidFileName(false)" @template-modal-close="onTemplateModalClose" @template-modal-confirm="onTemplateModalConfirm"> </router-view> {% endblock %} -
Deprecated all
sw_sales_channel_detail_base_general_input_*_selectionandsw_sales_channel_detail_base_general_input_*_assignmentblocks fromsw-sales-channel-detail-basefromsw-sales-channel-detail-base-view component due to its tight coupling. Components overriding this blocks must now override the complete row in the form by overridingsw_sales_channel_detail_base_general_input_*blocks.- Deprecated
sw_sales_channel_detail_base_general_input_payments_methods_selectionusesw_sales_channel_detail_base_general_input_payments_methodsinstead - Deprecated
sw_sales_channel_detail_base_general_input_payments_methods_assignmentusesw_sales_channel_detail_base_general_input_payments_methodsinstead - Deprecated
sw_sales_channel_detail_base_general_input_shipping_methods_selectionusesw_sales_channel_detail_base_general_input_shipping_methodsinstead - Deprecated
sw_sales_channel_detail_base_general_input_shipping_methods_assignmentusesw_sales_channel_detail_base_general_input_shipping_methodsinstead - Deprecated
sw_sales_channel_detail_base_general_input_countries_selectionusesw_sales_channel_detail_base_general_input_countriesinstead - Deprecated
sw_sales_channel_detail_base_general_input_countries_assignmentusesw_sales_channel_detail_base_general_input_countriesinstead - Deprecated
sw_sales_channel_detail_base_general_input_currencies_selectionusesw_sales_channel_detail_base_general_input_currenciesinstead - Deprecated
sw_sales_channel_detail_base_general_input_currencies_assignmentusesw_sales_channel_detail_base_general_input_currenciesinstead - Deprecated
sw_sales_channel_detail_base_general_input_languages_selectionusesw_sales_channel_detail_base_general_input_languagesinstead - Deprecated
sw_sales_channel_detail_base_general_input_languages_assignmentusesw_sales_channel_detail_base_general_input_languagesinstead
- Deprecated
Changes
- A theme must now implement the
Shopware\Storefront\Framework\ThemeInterface. - If your javascript lives in
Resources/storefront/scriptyou have to explicitly define this path in thegetStorefrontScriptPath()method of your plugin base class as we have changed the default path toResources/dist/storefront/js. - Added
extractIdsToUpdatetoShopware\Storefront\Framework\Seo\SeoUrlRoute\SeoUrlRouteInterface.extractIdsToUpdatemust provide the ids of entities which seo urls should be updated based on an EntityWrittenContainerEvent. - Replace
productUrl(product)withseoUrl('frontend.detail.page', {'productId': product.id}) }andnavigationUrl(navigation)withseoUrl('frontend.navigation.page', { 'navigationId': navigation.id })' - The JavaScript
CmsSlotReloadPluginis no longer used to render the response after paginating a product list. This has been moved to theListingPluginwhich can be found inplatform/src/Storefront/Resources/src/script/plugin/listing/listing.plugin.js.- The
ListingPluginnow handles the pagination as well as the product filter and the new sorting element. - The pagination uses a separate JavaScript plugin
listing-sorting.plugin.js.
- The
- We simplified the implementation of the
\Shopware\Storefront\Framework\Cache\CacheWarmer\CacheRouteWarmer- The class is now an interface instead of an abstract class
- It is no longer necessary to implement your own
WarmUpMessageclass - It is no longer necessary to register your class as message queue handler
- Removed the
handlefunction removed without any replacement - The
\Shopware\Storefront\Framework\Cache\CacheWarmer\WarmUpMessagenow expects the route name and a parameter list - See
\Shopware\Storefront\Framework\Cache\CacheWarmer\Product\ProductRouteWarmerfor detail information.
- We added two new environment variables
SHOPWARE_HTTP_CACHE_ENABLEDandSHOPWARE_HTTP_DEFAULT_TTLwhich have to be defined in your.envfile
SHOPWARE_HTTP_CACHE_ENABLED=1
SHOPWARE_HTTP_DEFAULT_TTL=7200-
We supports now the symfony http cache. You have to change the
index.phpof your project as follow:Before:
// resolves seo urls and detects storefront sales channels $request = $kernel->getContainer() ->get(RequestTransformerInterface::class) ->transform($request); $response = $kernel->handle($request);
After:
// resolves seo urls and detects storefront sales channels $request = $kernel->getContainer() ->get(RequestTransformerInterface::class) ->transform($request); $enabled = $kernel->getContainer()->getParameter('shopware.http.cache.enabled'); if ($enabled) { $store = $kernel->getContainer()->get(\Shopware\Storefront\Framework\Cache\CacheStore::class); $kernel = new \Symfony\Component\HttpKernel\HttpCache\HttpCache($kernel, $store, null, ['debug' => $debug]); } $response = $kernel->handle($request);
-
We moved the administration sources from Resources/administration to Resources/app/administration. This also applies to the expected plugin admin extensions.
-
CSRF implementation
- Every
POSTmethod needs to append a CSRF token now - CSRF tokens can be generated in twig or via ajax, if configured. Here is a small twig example for a typical form:
<form name="ExampleForm" method="post" action="{{ path("example.route") }}" data-form-csrf-handler="true"> <!-- some form fields --> {{ sw_csrf('example.route') }} </form>
- Important: The CSRF function needs the route name as parameter, because a token is only valid for a specific route in twig mode
- To prevent a CSRF check in a controller action you can set
"csrf_protected"=falseto thedefaultsin your route annotation:
/** * @Route("/example/route", name="example.route", defaults={"csrf_protected"=false}, methods={"POST"}) */
- Every
-
Removed abandoned TwigExtensions in favor of Twig Core Extra extensions
- Use
u.wordwrapandu.truncateinstead of thewordwrapandtruncatefilter. - Use the
format_dateorformat_datetimefilter instead of thelocalizeddatefilter - Take a look here for more information: https://github.com/twigphp/Twig-extensions
- Use
-
Removed the contact and newsletter page
- If you used the template
platform/src/Storefront/Resources/views/storefront/page/newsletter/index.html.twigorplatform/src/Storefront/Resources/views/storefront/page/contact/index.html.twigyour changes will not work anymore. You now find them here:platform/src/Storefront/Resources/views/storefront/element/cms-element-form. - The templates are part of the new
form cms elementthat can be used in layouts. Therefore all changes in these templates are applied wherever you use the cms element. - We added two
default shop page layoutsfor thecontactandnewsletter formin theshopping experienceswhere the cms form element is used. - These layouts have to be assigned in the
settingsunderbasic informationforcontact pagesandnewsletter pages. - The assigned layout for
contact pagesis used in the footerplatform/src/Storefront/Resources/views/storefront/layout/footer/footer.html.twigas modal.
- If you used the template
-
We split the
Storefront/Resources/views/storefront/layout/navigation/offcanvas/navigation.html.twigtemplate into smaller templates. If you have extended this template you should check if the blocks are still correctly overwritten. If this is not the case, you have to extend the smaller template file into which the block was moved. -
The data format of the
lineItem.payload.optionshas changed. Now there is a simple array per element withoptionandgroup. It contains the translated names of the entities. If you have changed the templatestorefront/page/checkout/checkout-item.html.twigyou have to change the following: Before:{% block page_checkout_item_info_variants %} {% if lineItem.payload.options|length >= 1 %} <div class="cart-item-variants"> {% for option in lineItem.payload.options %} <div class="cart-item-variants-properties"> <div class="cart-item-variants-properties-name">{{ option.group.translated.name }}:</div> <div class="cart-item-variants-properties-value">{{ option.translated.name }}</div> </div> {% endfor %} </div> {% endif %} {% endblock %}After:
{% block page_checkout_item_info_variants %} {% if lineItem.payload.options|length >= 1 %} <div class="cart-item-variants"> {% for option in lineItem.payload.options %} <div class="cart-item-variants-properties"> <div class="cart-item-variants-properties-name">{{ option.group }}:</div> <div class="cart-item-variants-properties-value">{{ option.option }}</div> </div> {% endfor %} </div> {% endif %} {% endblock %}- The following blocks moved from the
storefront/element/cms-element-product-listing.html.twigtemplate into the new templatestorefront/component/product/listing.html.twig. If you have overwritten one of the following blocks, you must now extend thestorefront/component/product/listing.html.twigtemplate instead of thestorefront/element/cms-element-product-listing.html.twigtemplateelement_product_listing_wrapper_contentelement_product_listing_pagination_nav_actionselement_product_listing_pagination_nav_topelement_product_listing_sortingelement_product_listing_rowelement_product_listing_colelement_product_listing_boxelement_product_listing_col_emptyelement_product_listing_col_empty_alertelement_product_listing_pagination_nav_bottom
- The
storefront/component/listing/filter-panel.html.twigcomponent requires now a providedsidebar (bool)parameter. Please provide this parameter in thesw_includetag:{% sw_include '@Storefront/storefront/component/listing/filter-panel.html.twig' with { listing: listing, sidebar: true } %}
- The following blocks moved from the
Changes
-
The env variables
SHOPWARE_SES_*were renamed toSHOPWARE_ES_*. -
If you used one of the elastic search parameter in your services.xml you have to change it as follow: Before:
<service ....> <argument>%shopware.ses.enabled%</argument> <argument>%shopware.ses.indexing.enabled%</argument> <argument>%shopware.ses.index_prefix%</argument> </service>After:
<service ....> <argument>%elasticsearch.enabled%</argument> <argument>%elasticsearch.indexing_enabled%</argument> <argument>%elasticsearch.index_prefix%</argument> </service> -
The extensions are now saved at the top level of the entities.
-
Now you have to change the ElasticsearchDefinition::getMapping for external resources.
Before:
'extensions' => [ 'type' => 'nested 'properties' => [ 'extensionsField' => $this->mapper->mapField($definition, $definition->getField('extensionsField'), $context) ] ]After:
'extensionsField' => $this->mapper->mapField($definition, $definition->getField('extensionsField'), $context) -
And you have to reindex.
-