Skip to content

[5.x]: Applying project config neglects to soft-delete structure, causing query to return duplicate entries #16450

@mmikkel

Description

@mmikkel

What happened?

Description

If a Structure section is converted to a Channel and then back again to a Structure, and this change (i.e. the section's structureId now pointing to a new structure) is later applied via project config in an environment where the section is still related to its original structure, Craft will neglect to (soft-)delete that original structure in the structures table.

I came across this when some entry queries started to return duplicate entries for a particular section (let's call this section sectionA). Specifically, any query that queried for entries in multiple sections like

{% set entries = craft.entries.section(['sectionA', 'sectionB']).all() %}

The culprit turned out to be the fact that entries in sectionA created prior to the section having its structureId changed via project config as per the above described scenario, now had multiple rows in the structureelements table that were related to multiple, non-soft-deleted rows in the structures table. Manually adding a value to the original structure's dateDeleted column in the structures table fixed the issue, confirming it as the cause for the duplicate entries being returned.

(BTW: Before figuring this out I tried running craft utils/repair/section-structure on the affected section; that did nothing in this case, but that's probably expected?).

Steps to reproduce

I'm able to reliably reproduce the issue with a clean Craft 5 install by following these steps:

  1. Create a Structure section (sectionA), and another section (sectionB; type doesn't matter)
  2. Create an entry in both sections
  3. Export a database dump
  4. Convert the sectionA Structure section to a Channel – then back to a Structure
  5. Import the database dump (the Structure section will now be back to it's original structureId)
  6. Run craft project-config/apply
  7. Execute a query like craft.entries.section(['sectionA', 'sectionB']).all() and confirm that the entry from sectionA is returned twice
  8. Add a value to the original structure's dateDeleted column in the structures table
  9. Re-run the query and confirm that the entry is no longer returned as a duplicate

Expected behavior

If an existing section becomes related to a new structure in the structures table, Craft should make sure that any existing structures for that section are (soft)deleted.

Actual behavior

Existing structures aren't soft-deleted if the section's structureId is updated via project config.

Craft CMS version

5.5.10

PHP version

8.3.14

Operating system and version

macOS; DDEV v1.24.1

Database type and version

MySQL 8.0.36

Image driver and version

No response

Installed plugins and versions

None

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions