- The
user
entity is now write protected via api. To create, update or delete auser
entity, you need theuser-verified
oauth-scope. The scope can be requested over the/api/oauth/token
route.$client->request('POST', '/api/oauth/token', [ 'grant_type' => 'password', 'client_id' => 'administration', 'username' => 'admin', 'password' => 'shopware', 'scope' => ['user-verified'], ]);
-
The usage of
entity
in theshopware.entity.definition
tag is deprecated and will be removed with 6.4.- Therefore change:
<tag name="shopware.entity.definition" entity="product"/>
To:<tag name="shopware.entity.definition"/>
- As a fallback, this function is used first
- Therefore change:
-
We deprecated the
LongTextWithHtmlField
in 6.2, useLongTextField
withAllowHtml
flag instead -
The Mailer is not overwritten anymore, instead the swiftmailer.transport is decorated.
- Therefore the MailerFactory returns a Swift_Transport Instance instead of Swift_Mailer
- The MailerFactory::create Method is deprecated now
Before:
new LongTextWithHtmlField('content', 'content')
After:
(new LongTextField('content', 'content'))->addFlags(new AllowHtml()
-
CartBehavior::isRecalculation is deprecated and will be removed in version 6.3
-
Please use context permissions instead:
- Permissions can be configured in the SalesChannelContext.
CartBehavior
is created based on the permissions fromSalesChannelContext
, you can check the permissions at this class.- Permissions exists:
ProductCartProcessor::ALLOW_PRODUCT_PRICE_OVERWRITES
ProductCartProcessor::SKIP_PRODUCT_RECALCULATION
DeliveryProcessor::SKIP_DELIVERY_RECALCULATION
PromotionCollector::SKIP_PROMOTION
- Define permissions for AdminOrders at class
SalesChannelProxyController
within the array constantADMIN_ORDER_PERMISSIONS
. - Define permissions for the Recalculation at class
OrderConverter
within the array constantADMIN_EDIT_ORDER_PERMISSIONS
. - Extended permissions with subscribe event
SalesChannelContextPermissionsChangedEvent
, see detail at classSalesChannelContextFactory
-
The usage of
$connection->executeQuery()
for write operations is deprecated, use$connection->executeUpdate()
instead. -
For the possibility to add individual product to across selling, you need to use a new field for creating a cross selling
- Please use type
productList
orproductStream
in order to create a corresponding cross selling
- Please use type
-
The
\Shopware\Core\Framework\DataAbstractionLayer\EntityExtensionInterface
will be removed, extend from the abstract class\Shopware\Core\Framework\DataAbstractionLayer\EntityExtension
instead. -
Deprecated
\Shopware\Core\Framework\Routing\RouteScopeInterface
use abstract class\Shopware\Core\Framework\Routing\AbstractRouteScope
instead -
Deprecated
\Shopware\Core\Content\ContactForm\ContactFormService
use the new service\Shopware\Core\Content\ContactForm\SalesChannel\ContactFormRoute
instead -
Deprecated
\Shopware\Core\Content\Product\SalesChannel\Listing\ProductListingGateway
use\Shopware\Core\Content\Product\SalesChannel\Listing\ProductListingRouteInterface
instead -
Deprecated
\Shopware\Core\Content\Product\SalesChannel\Listing\ProductListingGatewayInterface
use\Shopware\Core\Content\Product\SalesChannel\Listing\ProductListingRouteInterface
instead -
Deprecated
\Shopware\Core\Content\Product\SalesChannel\Search\ProductSearchGateway
use\Shopware\Core\Content\Product\SalesChannel\Search\ProductSearchRouteInterface
instead -
Deprecated
\Shopware\Core\Content\Product\SalesChannel\Search\ProductSearchGatewayInterface
use\Shopware\Core\Content\Product\SalesChannel\Search\ProductSearchRouteInterface
instead -
Deprecated
\Shopware\Core\Content\Product\SalesChannel\Suggest\ProductSuggestGatewayInterface
use\Shopware\Core\Content\Product\SalesChannel\Search\ProductSearchRouteInterface
instead -
Deprecated
\Shopware\Core\Content\Product\SalesChannel\Suggest\ProductSuggestGateway
use\Shopware\Core\Content\Product\SalesChannel\Search\ProductSearchRouteInterface
instead -
Deprecated
\Shopware\Core\Checkout\Customer\SalesChannel\AccountService
use one of the following new services\Shopware\Core\Checkout\Customer\SalesChannel\ChangeCustomerProfileRoute
\Shopware\Core\Checkout\Customer\SalesChannel\ChangeEmailRoute
\Shopware\Core\Checkout\Customer\SalesChannel\ChangePasswordRoute
\Shopware\Core\Checkout\Customer\SalesChannel\ChangePaymentMethodRoute
\Shopware\Core\Checkout\Customer\SalesChannel\LoginRoute
\Shopware\Core\Checkout\Customer\SalesChannel\LogoutRoute
\Shopware\Core\Checkout\Customer\SalesChannel\SendPasswordRecoveryMailRoute
\Shopware\Core\Checkout\Customer\SalesChannel\ResetPasswordRoute
-
Deprecated
\Shopware\Core\Content\Newsletter\NewsletterSubscriptionServiceInterface
and\Shopware\Core\Content\Newsletter\NewsletterSubscriptionService
use one of the following new services\Shopware\Core\Content\Newsletter\SalesChannel\NewsletterSubscribeRoute
\Shopware\Core\Content\Newsletter\SalesChannel\NewsletterUnsubscribeRoute
\Shopware\Core\Content\Newsletter\SalesChannel\NewsletterConfirmRoute
-
Deprecated
\Shopware\Core\Checkout\Customer\SalesChannel\AccountRegistrationService
use one of the following new services\Shopware\Core\Checkout\Customer\SalesChannel\RegisterRouteInterface
\Shopware\Core\Checkout\Customer\SalesChannel\RegisterConfirmRouteInterface
-
Deprecated
\Shopware\Core\Checkout\Promotion\Cart\Discount\DiscountPackagerInterface
use\Shopware\Core\Checkout\Promotion\Cart\Discount\DiscountPackager
instead -
Added optional second parameter
$context
to\Shopware\Core\Framework\Plugin\PluginManagementService::uploadPlugin
and\Shopware\Core\Framework\Plugin\PluginManagementService::deletePlugin
. It will be required in 6.3.0 -
Deprecated
\Shopware\Core\Framework\Plugin\PluginManagementService::extractPluginZip
which will be private in 6.3.0 -
Added optional third parameter
$definition
toShopware\Elasticsearch\Framework\ElasticsearchHelper::addTerm
. It will be required in 6.3.0. -
Added a new translatable
label
property to\Shopware\Core\Content\ImportExport\ImportExportProfileDefinition
- This property is required
- The name may be omitted now
-
Added new message
\Shopware\Core\Framework\Adapter\Cache\Message\CleanupOldCacheFolders
to cleanup old cache folders invar/cache
-
sw-settings-custom-field-set
- Removed method which overrides the mixin method
getList
, use the computedlistingCriteria
instead - Add computed property
listingCriteria
- Removed method which overrides the mixin method
-
sw-settings-document-list
- Removed method which overrides the mixin method
getList
, use the computedlistingCriteria
instead - Add computed property
listingCriteria
- Removed method which overrides the mixin method
-
Refactor
sw-settings-snippet-list
- Removed
StateDeprecated
- Remove computed property
snippetSetStore
, use `snippetSetRepository' instead - Add computed property
snippetSetRepository
- Add computed property
snippetSetCriteria
- Removed
-
Refactor
sw-settings-snippet-set-list
- Remove
StateDeprecated
- Remove computed property
snippetSetStore
, use `snippetSetRepository' instead - Add computed property
snippetSetCriteria
- The method
onConfirmClone
is now an asynchronous method
- Remove
-
Refactor mixin
sw-settings-list.mixin
- Remove
StateDeprecated
- Remove computed property
store
, useentityRepository
instead - Add computed property
entityRepository
- Add computed property
listingCriteria
- Remove
-
The component sw-plugin-box was refactored to use the "repositoryFactory" instead of "StateDeprecated" to fetch and save data - removed "StateDeprecated" - removed computed "pluginStore" use "pluginRepository" instead
-
The component sw-settings-payment-detail was refactored to use the "repositoryFactory" instead of "StateDeprecated" to fetch and save data
- removed "StateDeprecated"
- removed computed "paymentMethodStore" use "paymentMethodRepository" instead
- removed computed "ruleStore" use "ruleRepository" instead
- removed computed "mediaStore" use "mediaRepository" instead
-
Refactor the module
sw-settings-number-range-detail
- Remove LocalStore
- Remove StateDeprecated
- Remove data typeCriteria
- Remove data numberRangeSalesChannelsStore
- Remove data numberRangeSalesChannels
- Remove data numberRangeSalesChannelsAssoc
- Remove data salesChannelsTypeCriteria
- Remove computed numberRangeStore use numberRangeRepository instead
- Remove computed firstSalesChannel
- Remove computed salesChannelAssociationStore
- Remove computed numberRangeStateStore use numberRangeStateRepository instead
- Remove computed salesChannelStore use salesChannelRepository instead
- Remove computed numberRangeTypeStore use numberRangeTypeRepository instead
- Remove method onChange
- Remove method showOption
- Remove method getPossibleSalesChannels
- Remove method setSalesChannelCriteria
- Remove method enrichAssocStores
- Remove method onChangeSalesChannel
- Remove method configHasSaleschannel
- Remove method selectHasSaleschannel
- Remove method undeleteSaleschannel
-
Refactored
sw-newsletter-recipient-list
- Removed LocalStore
- Removed StateDeprecated
- Removed Computed salesChannelStore, use salesChannelRepository instead
- Removed Computed tagStore, use tagRepository instead
- Removed Computed tagAssociationStore
-
Refactored mapErrorService
- Deprecated
mapApiErrors
, usemapPropertyErrors
- Added
mapCollectionPropertyErrors
to mapErrorService for Entity Collections
- Deprecated
-
Deprecated
sw-multi-ip-select
with version 6.4, use thesw-multi-tag-ip-select
-component instead -
Replaced Store based datahandling with repository based datahandling in media specific components and modules, including the following changes
- sw-media-field is deprecated and replaced by sw-media-field-v2
- Added injection of
repositoryFactory
- Replaced computed property
mediaStore
withmediaRepository
- Method
fetchItem
is now async - Method
fetchSuggestions
is now async
- Added injection of
- sw-media-list-selection is deprecated and replaced by sw-media-list-selection-v2
- Added injection of
repositoryFactory
- Added injection of
mediaService
- Replaced computed property
mediaStore
withmediaRepository
- Method
onUploadsAdded
is now async - Method
successfulUpload
is now async
- Added injection of
- sw-media-preview is deprecated and replaced by sw-media-preview-v2
- Added injection of
repositoryFactory
- Replaced computed property
mediaStore
withmediaRepository
- Method
fetchSourceIfNecessary
is now async - Method
getDataUrlFromFile
is now async
- Added injection of
- sw-media-upload is deprecated and replaced sw-media-upload-v2
- Added injection of
repositoryFactory
- Added injection of
mediaService
- Replaced computed property
defaultFolderStore
withdefaultFolderRepository
- Watcher of prop
defaultFolder
is now async - Method
createdComponent
is now async - Method
onUrlUpload
is now async - Method
handleUpload
is now async - Method
getDefaultFolderId
is now async - Replaced method
handleUploadStoreEvent
withhandleMediaServiceUploadEvent
- Added injection of
- sw-duplicated-media is deprecated and replaced sw-duplicated-media-v2
- Added injection of
repositoryFactory
- Replaced computed property
mediaStore
withmediaRepository
- Replaced method
handleUploadStoreEvent
withhandleMediaServiceUploadEvent
- Method
updatePreviewData
is now async - Method
renameFile
is now async - Method
skipFile
is now async - Method
replaceFile
is now async
- Added injection of
- sw-media-modal is deprecated and replaced by sw-media-modal-v2
- Added injection of
repositoryFactory
- Added injection of
mediaService
- Replaced computed property
mediaStore
withmediaRepository
- Replaced computed property
mediaFolderStore
withmediaFolderRepository
- Method
fetchCurrentFolder
is now async - Method
onUploadsAdded
is now async - Method
onUploadsFinished
is now async
- Added injection of
- sw-upload-store-listener is deprecated and replaced by sw-upload-listener
- Added injection of
repositoryFactory
- Added injection of
mediaService
- Added computed property
mediaRepository
- Method
handleError
is now async
- Added injection of
- sw-media-compact-upload is deprecated and replaced by sw-media-compact-upload-v2
- sw-media-field is deprecated and replaced by sw-media-field-v2
-
Deprecated method
getFields
, usegetStructuredFields
instead -
Removed module
sw-settings-newsletter-config
and componentsw-settings-newsletter-config
-
Refactored settings items list
-
Deprecated
sw_settings_content_card_slot_plugins
twig block with version 6.4 -
If your Plugin extends
src/module/sw-settings/page/sw-settings-index/sw-settings-index.html.twig
to appear in the "plugins" tab on settings page, remove the extension from your plugin and add ansettingsItem
Array to your Module instead:Module.register('my-awesome-plugin') { // ... settingsItem: [ { name: 'my-awesome-plugin', // unique name to: 'my.awesome.plugin.index', // dot notated route to your plugin's settings index page label: 'my.awesome.plugin.title', // translation snippet key group: 'plugins', // register plugin under "plugins" tab in settings page icon: 'use a shopware icon here', // OR component: 'component' // use a component here, if you want to render the icon in any other way } // ... more settings items ] }
-
-
Implemented the possibility to add individual product to cross selling
- Added
sw-product-cross-selling-assignment
component
- Added
-
CustomFields are now sorted naturally when custom position is used with customFieldPosition (for example 1,9,10 instead of 1,10,9)
-
The id of checkbox and switch fields are now unique. Therefore you have to update your selectors if you want to get the fields by id. This was an important change, because every checkbox and switch field has the same id. This causes problems when you click on the corresponding label.
-
Added block
sw_sales_channel_detail_analytics_fields_anonymize_ip
tosw-sales-channel-detail-analytics.html.twig
- We removed the SCSS skin import
@import 'skin/shopware/base'
inside/Users/tberge/www/sw6/platform/src/Storefront/Resources/app/storefront/src/scss/base.scss
.-
If you don't use the
@Storefront
bundle in yourtheme.json
and you are importing the shopware corebase.scss
manually you have to import the shopware skin too in order to get the same result:Before
@import "../../../../vendor/shopware/platform/src/Storefront/Resources/app/storefront/src/scss/base.scss";
After
@import "../../../../vendor/shopware/platform/src/Storefront/Resources/app/storefront/src/scss/base.scss"; @import "../../../../vendor/shopware/platform/src/Storefront/Resources/app/storefront/src/scss/skin/shopware/base";
-
- We changed the storefront ESLint rule
comma-dangle
tonever
, so that trailing commas won't be forcefully added anymore - The theme manager supports now tabs. Usage:
"config": {
"tabs": {
"colors": {
"label": {
"en-GB": "Colours",
"de-DE": "Farben"
}
},
},
"blocks": {
...
},
...,
"fields": {
"sw-color-brand-primary": {
"label": {
"en-GB": "Primary colour",
"de-DE": "Primärfarbe"
},
"type": "color",
"value": "#008490",
"editable": true,
"block": "themeColors",
"tab": "colors",
"order": 100
},
...
}
}
When you don´t specify a tab then it will be shown in the main tab.
- Added Twig Filter
replace_recursive
for editing values in nested Arrays. This allows for editing js options in Twig:
{% 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
}
} %}
{% block element_product_slider_slider %}
<div class="base-slider"
data-product-slider="true"
data-product-slider-options="{{ productSliderOptions|json_encode }}">
</div>
{% endblock %}
Now the variable can be overwritten with replace_recursive
:
{% block element_product_slider_slider %}
{% set productSliderOptions = productSliderOptions|replace_recursive({
slider: {
mouseDrag: true
}
}) %}
{{ parent() }}
{% endblock %}
-
Added basic captcha support to the storefront
- Routes annotated with
@Captcha
will now require all active captchas to be valid - Captchas may be registered using the
shopware.storefront.captcha
tag and need to extend theAbstractCaptcha
class
- Routes annotated with
-
Added
HoneypotCaptcha
- This captcha checks wether a form field hidden from the user was filled out and stops the request if that's the case
- The
HoneypotCaptcha
is active by default
-
If you use the
widgets.search.pagelet
route in your template, you have to replace this withwidgets.search.pagelet.v2
:- Before:
url('widgets.search.pagelet', { search: page.searchTerm })
- After:
url('widgets.search.pagelet.v2')
- Before:
-
It is no longer possible to send requests against the
sales-channel-api
with theHttpClient
. You have to use theStoreApiClient
for this:- before:
import Plugin from 'src/plugin-system/plugin.class'; import HttpClient from 'src/service/http-client.service'; export default class MyStorefrontPlugin extends Plugin { init() { this.client = new HttpClient(); this.client.get('sales-channel-api-route', response => {}) } }
- after:
import Plugin from 'src/plugin-system/plugin.class'; import StoreApiClient from 'src/service/store-api-client.service'; export default class MyStorefrontPlugin extends Plugin { init() { this.client = new StoreApiClient(); this.client.get('sales-channel-api-route', response => {}); } }
- before:
-
Added block
component_head_analytics_tag_config
toanalytics.html.twig
With 6.2 we have refactored the implementation of the indexers. We had to make this decision because of two problems:
- the indexers were interdependent so that they always had to be executed one after the other and never in parallel.
- many indexers were not designed to be triggered by message queue and therefore ran into mysql deadlocks or indexed wrong data.
This now has the following consequences:
- all indexers in the shopware core have been marked as deprecated and will be removed in 6.3. Accordingly, new indexers have been implemented.
- if you have called the indexers, please use the new indexers. The old indexers are still available in the system, but without source code.
- if you have implemented your own indexer, you have to adapt it to the new system - as described below.
- if you are using DAL fields, which were previously filled automatically by an indexer, you will now have to write your own indexer - we have provided classes for this purpose. The following DAL fields are affected:
4.1
\Shopware\Core\Framework\DataAbstractionLayer\Field\ManyToManyIdField
4.2\Shopware\Core\Framework\DataAbstractionLayer\Field\TreeLevelField
4.3\Shopware\Core\Framework\DataAbstractionLayer\Field\TreePathField
4.4\Shopware\Core\Framework\DataAbstractionLayer\Field\ChildCountField
The new indexers work as follows:
- each indexer extends abstract
Shopware\Core\Framework\DataAbstractionLayer\Indexing\EntityIndexer
- each indexer takes care of indexing a whole entity
- if several data on an entity need to be indexed, a single indexer takes care of the successive updating of the data
- an event is thrown at the end of the indexer so that plugins can subscribe to the event to index additional data for this entity
- finally, the updated ids are passed to the
CacheClearer
to invalidate the corresponding caches
The base Shopware\Core\Framework\DataAbstractionLayer\Indexing\EntityIndexer
class looks as follows:
abstract class EntityIndexer
{
/**
* Returns a unique name for this indexer. This function is used for core updates
* if a indexer has to run after an update.
*/
abstract public function getName(): string;
/**
* Called when a full entity index is required. This function should generate a list of message for all records which
* are indexed by this indexer.
*/
abstract public function iterate($offset): ?EntityIndexingMessage;
/**
* Called when entities are updated over the DAL. This function should react to the provided entity written events
* and generate a list of messages which has to be processed by the `handle` function over the message queue workers.
*/
abstract public function update(EntityWrittenContainerEvent $event): ?EntityIndexingMessage;
/**
* Called over the message queue workers. The messages are the generated messages
* of the `self::iterate` or `self::update` functions.
*/
abstract public function handle(EntityIndexingMessage $message): void;
}
The iterate
and update
functions are intended to trigger indexing. The iterate
is triggered on a full index and the update
when an entity is written in the system.
In both cases, an EntityIndexingMessage
can be returned which is then either handled by the message queue or directly.
The following ProductIndexer
is intended to illustrate once again how such an indexer works. This indexer can be used as a template. Only the entity that is handled must be exchanged here.
<?php declare(strict_types=1);
namespace Shopware\Core\Content\Product\DataAbstractionLayer;
use Shopware\Core\Content\Product\Events\ProductIndexerEvent;
use Shopware\Core\Content\Product\ProductDefinition;
use Shopware\Core\Framework\Adapter\Cache\CacheClearer;
use Shopware\Core\Framework\DataAbstractionLayer\Dbal\Common\IteratorFactory;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenContainerEvent;
use Shopware\Core\Framework\DataAbstractionLayer\Indexing\EntityIndexer;
use Shopware\Core\Framework\DataAbstractionLayer\Indexing\EntityIndexingMessage;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
class ProductIndexer extends EntityIndexer
{
/** @var IteratorFactory */
private $iteratorFactory;
/** @var EntityRepositoryInterface */
private $repository;
/** @var CacheClearer */
private $cacheClearer;
/** @var EventDispatcherInterface */
private $eventDispatcher;
public function getName(): string
{
return 'product.indexer';
}
public function iterate($offset): ?EntityIndexingMessage
{
$iterator = $this->iteratorFactory->createIterator($this->repository->getDefinition(), $offset);
$ids = $iterator->fetch();
// loop end? return null
if (empty($ids)) {
return null;
}
return new EntityIndexingMessage($ids, $iterator->getOffset());
}
public function update(EntityWrittenContainerEvent $event): ?EntityIndexingMessage
{
$updates = $event->getPrimaryKeys(ProductDefinition::ENTITY_NAME);
if (empty($updates)) {
return null;
}
// update essential data immediately - one example can be the available stock of an product
return new EntityIndexingMessage($updates, null, $event->getContext());
}
public function handle(EntityIndexingMessage $message): void
{
$ids = $message->getData();
if (empty($ids)) {
return;
}
// update all required data
$this->eventDispatcher->dispatch(new ProductIndexerEvent($ids, $message->getContext()));
$this->cacheClearer->invalidateIds($ids, ProductDefinition::ENTITY_NAME);
}
}
In case you have used fields from the DAL, which filled automatically by an indexer, you have now to trigger the data indexing for this fields by yourself. We provide updater classes which you call from your indexer. However, we have adapted the previous indexers so that they will continue to update your entities automatically during the 6.2 version. If you have started the indexing process yourself, you can set a flag to prevent the old indexing process from working for your entity.
-
\Shopware\Core\Framework\DataAbstractionLayer\Field\ManyToManyIdField
- Can be updated by
\Shopware\Core\Framework\DataAbstractionLayer\Indexing\ManyToManyIdFieldUpdater
- Old indexing can be disabled by
public function hasManyToManyIdFields(): bool { return false; }
in your entity definition
- Can be updated by
-
\Shopware\Core\Framework\DataAbstractionLayer\Field\TreeLevelField
and\Shopware\Core\Framework\DataAbstractionLayer\Field\TreePathField
- can be updated by
\Shopware\Core\Framework\DataAbstractionLayer\Indexing\TreeUpdater
- Old indexing can be disabled by
public function isTreeAware(): bool { return false; }
in your entity definition
- can be updated by
-
\Shopware\Core\Framework\DataAbstractionLayer\Field\ChildCountField
- can be updated by
\Shopware\Core\Framework\DataAbstractionLayer\Indexing\ChildCountUpdater
- Old indexing can be disabled by
public function isChildCountAware(): bool { return false; }
in your entity definition
- can be updated by
Simply create an indexer as above, inject the service and call it when your entity is updated.
If you have implemented your own seo urls in the system, you will have to initiate the indexing of these urls yourself in the future. For the 6.2 version we have implemented a flag which defines whether the old indexing process should continue to work.
To update the seo urls correctly you need an indexer which indexes the entity behind the seo url. If there is already an indexer for the entity in the core you can register yourself on the indexer event, otherwise you have to write your own indexer.
In the indexer you can use the \Shopware\Core\Content\Seo\SeoUrlUpdater
service to generate the seo urls. Here is an example how to call it:
<?php declare(strict_types=1);
namespace Shopware\Storefront\Framework\Seo\SeoUrlRoute;
use Shopware\Core\Content\Product\Events\ProductIndexerEvent;
use Shopware\Core\Content\Product\ProductEvents;
use Shopware\Core\Content\Seo\SeoUrlUpdater;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class SeoUrlUpdateListener implements EventSubscriberInterface
{
/**
* @var SeoUrlUpdater
*/
private $seoUrlUpdater;
public function __construct(SeoUrlUpdater $seoUrlUpdater)
{
$this->seoUrlUpdater = $seoUrlUpdater;
}
public static function getSubscribedEvents()
{
return [
ProductEvents::PRODUCT_INDEXER_EVENT => 'updateProductUrls',
];
}
public function updateProductUrls(ProductIndexerEvent $event): void
{
$this->seoUrlUpdater->update('frontend.detail.page', $event->getIds());
}
}
To disable the old indexing process for a seo url route you have to set the flag \Shopware\Core\Content\Seo\SeoUrlRoute\SeoUrlRouteConfig::$supportsNewIndexer
. This is easily done in the getConfig
method of your SeoUrlRoute
class.
public function getConfig(): SeoUrlRouteConfig
{
return new SeoUrlRouteConfig(
$this->productDefinition,
self::ROUTE_NAME,
self::DEFAULT_TEMPLATE,
true,
true // disable old indexing
);
}
Since the new indexers are now designed to be processed in parallel via the message queue, MySQL deadlocks can quickly occur when the same table is written by different processes. For this we have provided the helper class \Shopware\Core\Framework\DataAbstractionLayer\Doctrine\RetryableQuery
.
This offers various possibilities to avoid a MySQL deadlock:
$query = new RetryableQuery(
$this->connection->prepare('UPDATE product SET active = :active WHERE id = :id')
);
$query->execute(['id' => $id, 'active' => 1]);
$query = $this->connection->createQueryBuilder();
$query->update('...');
RetryableQuery::executeBuilder($query);
RetryableQuery::retryable(function() {
$this->connection->executeUpdate('...');
});