-
\Shopware\Storefront\Controller\StorefrontController::forwardToRoute
now handles parameters correctly. -
Request scope changes during a single request handle are prohibited.
-
Use
\Shopware\Core\Framework\Routing\RequestTransformerInterface::extractInheritableAttributes
if 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::CONTROLLER
Event-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\RouteScopeWhitlistCollectEvent
in 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
getJoinBuilder
method. -
\Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria
association handlingWe removed the
$criteria
parameter from theaddAssociation
function. By setting the criteria object the already added criteria was overwritten. This led to problems especially with multiple extensions by plugins. Furthermore the functionaddAssociationPath
was removed from the criteria. The following functions are now available on the criteria object:-
addAssociation(string $path): self
This 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): Criteria
This 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 thepartial
method. 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();
-
ValueCountAggregation
andValueAggregation
removed, useTermsAggregation
instead.$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::getResult
toAggregationResultCollection
-
We removed the
Aggregation::groupByFields
andAggregation::filters
property, use\Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\FilterAggregation
and\Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\TermsAggregation
instead$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
ClassLoader
with 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::boot
method was removed. Instead, use theStaticKernelPluginLoader
with an empty list. -
If you have implemented a custom
Shopware\Core\Framework\DataAbstractionLayer\FieldSerializer\AbstractFieldSerializer
, you must now provide aDefinitionInstanceRegistry
when calling the super constructor -
Removed
Shopware\Core\Framework\DataAbstractionLayer\EntityWrittenContainerEvent::getEventByDefinition
. UsegetEventByEntityName
instead, which takes the entity name instead of the entity classname but proved the same functionality. -
Removed
getDefinition
and the correspondingdefinition
member from\Shopware\Core\Framework\DataAbstractionLayer\EntityWriteResults
and...\Event\EntityWrittenEvent
. Classes which used this function can access the name of the written entity via the new methodgetEntityName
and retrieve the definition using theDefinitionInstanceRegistry
-
Replace service id
shopware.cache
withcache.object
-
If you invalidated the entity cache over the
shopware.cache
service, use the\Shopware\Core\Framework\Adapter\Cache\CacheClearer
instead. -
All customer events in
Shopware\Core\Checkout\Customer\Event
now get theShopware\Core\Syste\SalesChannel\SalesChannelContext
instead ofShopware\Core\Framework\Context
and asalesChannelId
-
Implement
getName
for 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::$trackingCode
has been replaced with\Shopware\Core\Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryEntity::$trackingCodes
. -
Add Bearer Auth Token to requests to
/api/v{version}/_info/entity-schema.json
and/api/v{version}/_info/business-events.json
routes -
Removed
shopware.api.api_browser.public
config value, useshopware.api.api_browser.auth_required = true
instead, to limit access to the open api routes -
Replace
product/category.extensions.seoUrls
withproduct/category.seoUrls
-
Dropped
additionalText
column of product entity, usemetaDescription
instead -
If your entity definition overwrites the
\Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition::getDefaults
method, 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\Acl
withShopware\Core\Framework\Api\Acl
-
Find and replace
Shopware\Core\Framework\CustomField
withShopware\Core\System\CustomField
-
Find and replace
Shopware\Core\Framework\Language
withShopware\Core\System\Language
-
Find and replace
Shopware\Core\Framework\Snippet
withShopware\Core\System\Snippet
-
Find and replace
Shopware\Core\Framework\Doctrine
withShopware\Core\Framework\DataAbstractionLayer\Doctrine
-
Find and replace
Shopware\Core\Framework\Pricing
withShopware\Core\Framework\DataAbstractionLayer\Pricing
-
Find and replace
Shopware\Core\Framework\Version
withShopware\Core\Framework\DataAbstractionLayer\Version
-
Find and replace
Shopware\Core\Framework\Faker
withShopware\Core\Framework\Demodata\Faker
-
Find and replace
Shopware\Core\Framework\PersonalData
withShopware\Core\Framework\Demodata\PersonalData
-
Find and replace
Shopware\Core\Framework\Logging
withShopware\Core\Framework\Log
-
Find and replace
Shopware\Core\Framework\ScheduledTask
withShopware\Core\Framework\MessageQueue\ScheduledTask
-
Find and replace
Shopware\Core\Framework\Twig
withShopware\Core\Framework\Adapter\Twig
-
Find and replace
Shopware\Core\Framework\Asset
withShopware\Core\Framework\Adapter\Asset
-
Find and replace
Shopware\Core\Framework\Console
withShopware\Core\Framework\Adapter\Console
-
Find and replace
Shopware\Core\Framework\Cache
withShopware\Core\Framework\Adapter\Cache
-
Find and replace
Shopware\Core\Framework\Filesystem
withShopware\Core\Framework\Adapter\Filesystem
-
Find and replace
Shopware\Core\Framework\Translation
withShopware\Core\Framework\Adapter\Translation
-
Find and replace
Shopware\Core\Framework\Seo
withShopware\Core\Content\Seo
-
Find and replace
Shopware\Core\Content\DeliveryTime
withShopware\Core\System\DeliveryTime
-
Find and replace
Shopware\Core\Framework\Context\
withShopware\Core\Framework\Api\Context\
-
Find and replace
Shopware\Core\System\User\Service\UserProvisioner
withShopware\Core\System\User\Service\UserProvisioner
- Warning: Do not replace
Shopware\Core\Framework\Context
withShopware\Core\Framework\Api\Context
, this would replace theFramework\Context.php
usage.
- Warning: Do not replace
-
Added unique constraint for
iso_code
column ofcurrency
table. The migration can fail if there are already duplicateiso_codes
in the table -
Replace
mailer
usage withcore_mailer
in your service definitions. -
If you call
\Shopware\Core\Framework\Api\Response\ResponseFactoryInterface::createDetailResponse
or\Shopware\Core\Framework\Api\Response\ResponseFactoryInterface::createListingResponse
in your plugin, the first parameter to be passed now is theCriteria
object with which the data was loaded. -
We changed the type hint of
Shopware\Core\Framework\Validation\ValidationServiceInterface::buildCreateValidation
andShopware\Core\Framework\Validation\ValidationServiceInterface::buildUpdateValidation
toSalesChannelContext
-
Replace
\Shopware\Core\Framework\Plugin::getExtraBundles
with\Shopware\Core\Framework\Plugin::getAdditionalBundles
. Dont use both. -
We implemented the new
Shopware\Core\HttpKernel
class which simplifies the kernel initialisation. This kernel can simply initialed and can be used in yourindex.php
file 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\SeoUrlGenerator
in your sources, please use thegenerate
function instead of thegenerateSeoUrls
-
If you update your decoration implementations of
\Shopware\Core\Framework\Validation\ValidationServiceInterface
to\Shopware\Core\Framework\Validation\DataValidationFactoryInterface
make 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::getEntityTag
signature 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 $entityName
type hint will be added withv6.3.0
- Before:
-
We'll stop deleting the plugin migration data on plugin uninstall in
v6.3.0
If 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 availableShopware
object 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 globalShopware
object. 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
editorFormat
andcolorCallback
got replaced withcolorOutput
- the default value for the property
alpha
is 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/select
divided in the subfoldersbase
andentity
base
contains the base components for creating new selects and the staticsw-single-select
andsw-multi-select
entity
contains components working with the api such assw-entity-multi-select
orsw-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
Shopware
and all childrenBefore:
import Application from 'src/core/shopware';
After:
const Application = Shopware.Application;
-
Important Change:
context
is now only available inservice
Before:
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\Criteria
and the Admin criteriasrc/core/data-new/criteria.data.js
-
Removed
addAssociationPath
-
Changed signature of
addAssociation
addAssociation(path);
-
addAssociation
now 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
getAssociation
function 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-sections
for 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
StateDeprecated
Before:
Shopware.State
After:
Shopware.StateDeprecated
-
Refactored the multiple inheritance of vuejs components and
$super
method with a breaking change!The syntax for the
$super
call has been changed as follows.this.$super('relatedMethodName', ...args);
You can use
$super
for 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_view
block.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_*_selection
andsw_sales_channel_detail_base_general_input_*_assignment
blocks fromsw-sales-channel-detail-base
fromsw-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_selection
usesw_sales_channel_detail_base_general_input_payments_methods
instead - Deprecated
sw_sales_channel_detail_base_general_input_payments_methods_assignment
usesw_sales_channel_detail_base_general_input_payments_methods
instead - Deprecated
sw_sales_channel_detail_base_general_input_shipping_methods_selection
usesw_sales_channel_detail_base_general_input_shipping_methods
instead - Deprecated
sw_sales_channel_detail_base_general_input_shipping_methods_assignment
usesw_sales_channel_detail_base_general_input_shipping_methods
instead - Deprecated
sw_sales_channel_detail_base_general_input_countries_selection
usesw_sales_channel_detail_base_general_input_countries
instead - Deprecated
sw_sales_channel_detail_base_general_input_countries_assignment
usesw_sales_channel_detail_base_general_input_countries
instead - Deprecated
sw_sales_channel_detail_base_general_input_currencies_selection
usesw_sales_channel_detail_base_general_input_currencies
instead - Deprecated
sw_sales_channel_detail_base_general_input_currencies_assignment
usesw_sales_channel_detail_base_general_input_currencies
instead - Deprecated
sw_sales_channel_detail_base_general_input_languages_selection
usesw_sales_channel_detail_base_general_input_languages
instead - Deprecated
sw_sales_channel_detail_base_general_input_languages_assignment
usesw_sales_channel_detail_base_general_input_languages
instead
- Deprecated
Changes
- A theme must now implement the
Shopware\Storefront\Framework\ThemeInterface
. - If your javascript lives in
Resources/storefront/script
you 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
extractIdsToUpdate
toShopware\Storefront\Framework\Seo\SeoUrlRoute\SeoUrlRouteInterface
.extractIdsToUpdate
must 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
CmsSlotReloadPlugin
is no longer used to render the response after paginating a product list. This has been moved to theListingPlugin
which can be found inplatform/src/Storefront/Resources/src/script/plugin/listing/listing.plugin.js
.- The
ListingPlugin
now 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
WarmUpMessage
class - It is no longer necessary to register your class as message queue handler
- Removed the
handle
function removed without any replacement - The
\Shopware\Storefront\Framework\Cache\CacheWarmer\WarmUpMessage
now expects the route name and a parameter list - See
\Shopware\Storefront\Framework\Cache\CacheWarmer\Product\ProductRouteWarmer
for detail information.
- We added two new environment variables
SHOPWARE_HTTP_CACHE_ENABLED
andSHOPWARE_HTTP_DEFAULT_TTL
which have to be defined in your.env
file
SHOPWARE_HTTP_CACHE_ENABLED=1
SHOPWARE_HTTP_DEFAULT_TTL=7200
-
We supports now the symfony http cache. You have to change the
index.php
of 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
POST
method 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"=false
to thedefaults
in 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.wordwrap
andu.truncate
instead of thewordwrap
andtruncate
filter. - Use the
format_date
orformat_datetime
filter instead of thelocalizeddate
filter - 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.twig
orplatform/src/Storefront/Resources/views/storefront/page/contact/index.html.twig
your 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 element
that 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 layouts
for thecontact
andnewsletter form
in theshopping experiences
where the cms form element is used. - These layouts have to be assigned in the
settings
underbasic information
forcontact pages
andnewsletter pages
. - The assigned layout for
contact pages
is used in the footerplatform/src/Storefront/Resources/views/storefront/layout/footer/footer.html.twig
as modal.
- If you used the template
-
We split the
Storefront/Resources/views/storefront/layout/navigation/offcanvas/navigation.html.twig
template 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.options
has changed. Now there is a simple array per element withoption
andgroup
. It contains the translated names of the entities. If you have changed the templatestorefront/page/checkout/checkout-item.html.twig
you 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.twig
template 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.twig
template instead of thestorefront/element/cms-element-product-listing.html.twig
templateelement_product_listing_wrapper_content
element_product_listing_pagination_nav_actions
element_product_listing_pagination_nav_top
element_product_listing_sorting
element_product_listing_row
element_product_listing_col
element_product_listing_box
element_product_listing_col_empty
element_product_listing_col_empty_alert
element_product_listing_pagination_nav_bottom
- The
storefront/component/listing/filter-panel.html.twig
component requires now a providedsidebar (bool)
parameter. Please provide this parameter in thesw_include
tag:{% 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.
-