filament-adjacency-list icon indicating copy to clipboard operation
filament-adjacency-list copied to clipboard

[Bug]: Double entries with relationships

Open RibesAlexandre opened this issue 1 year ago • 2 comments
trafficstars

What happened?

Hi,

I've 3 entries in my navigation_items database, a hasMany relationship to Navigation Model like this :

Navigation Model Table :

Capture d’écran 2024-09-28 à 17 00 48

NavigationItem Model Table :

Capture d’écran 2024-09-28 à 17 04 59

My Navigation Model is like this :

class Navigation extends Model
{
    use \Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships;

    protected $fillable = [
        'name' ,
        'slug' ,
        'position' ,
        //'items'
    ];

    public function items(): HasMany
    {
        return $this->hasMany(NavigationItem::class, 'navigation_id')
            ->whereNull('parent_id')
            ->with('childrenAndSelf')
            ->orderBy('sort');
    }

And this is my NavigationItemModel :

class NavigationItem extends Model
{
    use \Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships;

    protected $fillable = ['navigation_id', 'parent_id', 'label', 'sort', 'data'];

    public $timestamps = false;

    protected $casts = [
        'data' => 'array'
    ];

    public function children(): HasMany
    {
        return $this->hasMany(NavigationItem::class, 'parent_id')
            ->with('children')
            ->orderBy('sort');
    }

My AdjencyList is like this :

AdjacencyList::make('items')
                            ->label(__('filament-starter::translations.labels.items'))
                            ->visible(fn($record) => $record !== null)
                            ->relationship('items')
                            //->childrenKey('children')
                            ->maxDepth(config('filament-starter.navigation.settings.max_depth', 6))
                            ->form([ // etc...

When I comment ->whereNull('parent_id') from my Navigation Model, items appears twice, like this :

Capture d’écran 2024-09-28 à 17 03 38

But when I keep it, only parents appears :

Capture d’écran 2024-09-28 à 17 04 21

I don't understand if it's a bug or If I do a mistake. I followed the documentation and got this. Any explications ?

Thanks you

How to reproduce the bug

Just create 2 models related with tables :

Navigation

        Schema::create('navigations' , function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('slug')->unique();
            $table->string('position');
            $table->timestamps();
        });
}

// NavigationModel 

    use \Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships;

    protected $fillable = [
        'name' ,
        'slug' ,
        'position' ,
        //'items'
    ];

    public function items(): HasMany
    {
        return $this->hasMany(NavigationItem::class, 'navigation_id')
            ->whereNull('parent_id')
            ->with('childrenAndSelf')
            ->orderBy('sort');
    }

NavigationItem :

Schema::create('navigation_items' , function (Blueprint $table) {
            $table->id();
            $table->string('label');
            $table->foreignId('navigation_id')->constrained()->references('id')->on('navigations')->cascadeOnDelete();
            $table->foreignId('parent_id')->constrained()->references('id')->on('navigation_items')->cascadeOnDelete();
            $table->json('data');
            $table->string('sort');
        });

// NavigationItemModel 
    use \Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships;

    protected $fillable = ['navigation_id', 'parent_id', 'label', 'sort', 'data'];

    public $timestamps = false;

    protected $casts = [
        'data' => 'array'
    ];

    public function children(): HasMany
    {
        return $this->hasMany(NavigationItem::class, 'parent_id')
            ->with('children')
            ->orderBy('sort');
    }

And NavigationResource :

AdjacencyList::make('items')
                        ->label(__('filament-starter::translations.labels.items'))
                        ->visible(fn($record) => $record !== null)
                        ->relationship('items')
                        //->childrenKey('children')
                        ->maxDepth(config('filament-starter.navigation.settings.max_depth', 6))
                        ->form([
                            Forms\Components\TextInput::make('label')
                                ->label('Label')
                                ->required(),
                               ->options(['external_link' => 'External Link']),

                            Forms\Components\Group::make()
                                ->statePath('data')
                                ->whenTruthy('type')
                                ->live()
                                ->schema([
                                    Forms\Components\TextInput::make('url')
                                        ->label(__('filament-starter::translations.labels.url'))
                                        ->required(),
                                    Forms\Components\Select::make('target')
                                        ->label(__('filament-starter::translations.navigation.select-options.target'))
                                        ->options([
                                            '' => __('filament-starter::translations.navigation.select-options.same-tab'),
                                            '_blank' => __('filament-starter::translations.navigation.select-options.new-tab'),
                                        ])
                                        ->live()
                                        ->default(''),
                                ]),

Package Version

2.2.1

PHP Version

8.2.0

Laravel Version

^11

Which operating systems does with happen with?

macOS

Notes

No response

RibesAlexandre avatar Sep 28 '24 15:09 RibesAlexandre

i got the same bug too

maisur avatar Oct 17 '24 09:10 maisur

I encountered the same issue, and after investigating, it seems the root cause is related to how the nested tree is retrieved.

In staudenmeir/laravel-adjacency-list, the correct way to get a nested tree structure is by using: Model::tree()->get()->toTree(); However, in this package, the method being used is: Model::get()->toTree();

This causes the nested tree to return duplicated data, which is unintended.

After looking into the issue further, I found a helpful suggestion from the author in this GitHub issue discussion.

To fix this, you can modify your relationship method like this:

->relationship('items', fn($query, $record) => 
    $query->getRelation('children')
          ->getRelated()::treeOf(fn($queryTree) => 
              $queryTree->isRoot()->where('navigation_id', $record->id)
          )
)

Additionally, I can confirm that this solution works in v4.0.0-beta1. I recommend considering this version, as it is backward compatible.

chickgit avatar Oct 17 '24 16:10 chickgit