Skip to content

Deleting a child collection with right-side siblings corrupts _lft and _rgt values when the Collection model is replaced via ModelManifest #2421

@Huncsuga

Description

@Huncsuga
  • Lunar version: 1.3.0
  • Laravel Version: 12.55.1
  • PHP Version: 8.3.29
  • Database Driver & Version: MySQL 8.4

Expected Behaviour:

When deleting a child collection from a nested collection tree, the nested set values (_lft and _rgt) should be recalculated correctly.

For example, if a collection has multiple child collections and one of the middle children is deleted, the remaining right-side siblings should keep valid and unique _lft / _rgt values according to the nested set structure.

This should also work correctly when the Lunar Collection model is replaced with a custom application model as described in the documentation.

Actual Behaviour:

If the Collection model is replaced with a custom model extending \Lunar\Models\Collection, deleting a child collection that has at least two right-side siblings corrupts the nested set values.

After the deletion, the _lft and _rgt values of the right-side siblings become invalid / duplicated, as shown in the screenshots.

Image

Example setup:

  • Create several root collections.
  • Create multiple child collections under collection 2.
  • Delete one of the middle child collections (for example the second child), while it has at least two siblings to its right.

Result:

  • The remaining right-side siblings end up with broken _lft / _rgt values.

This issue only appears when the Collection model is overridden/replaced via ModelManifest.

Steps To Reproduce:

  1. Replace the Lunar Collection model with a custom application model:
namespace App\Models;

class Collection extends \Lunar\Models\Collection
{
    public function someCustomMethod()
    {
        return 'Hello!';
    }
}
  1. Register the replacement in a service provider:
public function boot()
{
    \Lunar\Facades\ModelManifest::replace(
        \Lunar\Models\Contracts\Collection::class,
        \App\Models\Collection::class,
    );
}
  1. Create a few root collections.
  2. Create multiple child collections under one parent collection (for example under collection 2).
    Example:
Image
  1. Delete one of the middle child collections, ensuring it has at least two siblings to its right.
  2. Check the _lft and _rgt values in the database.

Additional Notes:

I investigated this a bit and it seems related to the kalnoy/nestedset package.

In NodeTrait::bootNodeTrait, the relevant logic appears to run twice for the same model during deletion. On the first execution, the database values are still correct. On the second execution, the _lft / _rgt values become corrupted.

I confirmed this by adding debug output:

If I use dump(), I can see the same model being processed twice.
If I use dd() on the first execution, the process stops and the database values remain correct.

This suggests the second execution is what breaks the tree.

I traced this further and it seems the issue may be caused by Lunar's HasModelExtending trait, where fireModelEvent is overridden. My assumption is that when the Collection model is extended/replaced, the deleted event may be dispatched twice, which then causes the nested set deletion logic to run twice as well.

I am opening this bug report because I managed to narrow the issue down to this point, but I am not sure what the correct fix should be. Hopefully this investigation helps.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions