seo_url entries can remain canonical after being marked as deleted
- 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
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