shopware / shopware/shopware

seo_url entries can remain canonical after being marked as deleted

Open
#17,631 0 comments 0 reactions 0 assignees View on GitHub
domain/inventory Platform(Default) priority/low
Dominant language
PHP
Stars
3.4k
Forks
1.2k
Avg merge
3d 55m
Merged PRs (30d)
436

Description

### Shopware Version

6.7.8.2

### Affected area / extension

Platform(Default), SEO URL lifecycle

### Actual behaviour

When SEO URLs are marked as deleted through SeoUrlPersister::markAsDeleted(), only the is_deleted flag is updated.

Current behaviour:
```php
src/Core/Content/Seo/SeoUrlPersister.php

private function markAsDeleted(bool $deleted, array $ids, ?string $salesChannelId): void
{
...
$query = $this->connection->createQueryBuilder()
->update('seo_url')
->set('is_deleted', $deleted ? '1' : '0')
->where('foreign_key IN (:fks)')
...
}
```
If an affected row was previously canonical, it can remain in this state:
```txt
is_canonical = 1
is_deleted = 1
```
This means a deleted SEO URL can still occupy the canonical slot from the database/index perspective, even though runtime queries often treat deleted rows as inactive.

The unique key does not include is_deleted:
```mysql
UNIQUE KEY uniq.seo_url.foreign_key
(language_id, sales_channel_id, foreign_key, route_name, is_canonical)
```
At the same time, other queries in the persister, for example updateCanonicalSeoUrls(), explicitly filter with is_deleted = 0 when looking for a valid non-canonical replacement.

This creates an inconsistent state for deleted canonical SEO URL rows.

### Expected behaviour

When a SEO URL is marked as deleted, it should no longer remain canonical.

A deleted row should either be cleaned up by a dedicated lifecycle process or markAsDeleted() should reset is_canonical when $deleted === true.

### Technical context

The related method obsoleteIds() already resets is_canonical to NULL:
```php
private function obsoleteIds(array $ids, ?string $salesChannelId): void
{
...
$query = $this->connection->createQueryBuilder()
->update('seo_url')
->set('is_canonical', 'NULL')
->where('id IN (:ids)')
...
}
```
The same semantic rule could apply when marking SEO URLs as deleted.

### Related issue

This is related to, but separate from:

https://github.com/shopware/shopware/issues/9642

That issue addressed deleted SEO URLs being considered by the resolver. This report is about the remaining data lifecycle issue where deleted SEO URLs can still keep is_canonical = 1 and are not cleaned up.

### Possible solution

Adjust markAsDeleted() so that deleted SEO URLs are no longer canonical:
```php
->set('is_deleted', $deleted ? '1' : '0')
```
and when $deleted === true also set:
```php
is_canonical = NULL
```
Before applying this change, possible side effects should be checked for product restore flows, SEO URL history and redirects.

Contributor guide

Open the contributing guide

Research direction

Start in src/Core/Content/Seo/SeoUrlPersister.php at markAsDeleted(), then compare its update with obsoleteIds() and updateCanonicalSeoUrls(). Trace product restore, SEO URL history, and redirect flows before changing the deletion lifecycle. Done means deleted SEO URL rows no longer remain canonical while restoration and other lifecycle behavior still works.

Written by the indexing model from the issue text.

Assessment

Tech stack
mysql, php
Domain
backend, databases
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
75/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.