You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel Livewire分类组件异常:输入价格后仅显示父分类

分类嵌套展示异常:输入价格后仅显示父分类的解决方法

问题描述

实现了分类及其子分类的嵌套展示,基础功能正常,但输入最低价格或最高价格触发页面更新时,仅显示父分类,子分类消失。


现有代码

<div class="bg-white p-6 border border-border-white rounded-md hidden md:flex md:flex-col md:gap-6 md:max-w-[270px] h-fit">
    <div>
       <p class="capitalize font-semibold">type</p>
       <ul class="mt-3 flex flex-col gap-1">
           <li class="flex items-center gap-2">
               <input type="radio" id="any" wire:model="type" class="accent-purple w-4 h-4" value="any" wire:click="type">
               <label for="any" class="capitalize">any</label>
           </li>
           <li class="flex items-center gap-2">
               <input type="radio" id="auction" wire:model="type" class="accent-purple w-4 h-4" value="auction" wire:click="type">
               <label for="auction" class="capitalize">Auction</label>
           </li>
           <li class="flex items-center gap-2">
               <input type="radio" id="classified" wire:model="type" class="accent-purple w-4 h-4" value="classified" wire:click="type">
               <label for="classified" class="capitalize">classified</label>
           </li>
           selected {{$type}}
       </ul>
   </div> 
    <div>
       <p class="capitalize font-semibold">category</p>
       <ul class="mt-3 flex flex-col gap-1">
           <li class="flex items-center gap-2">
               <input type="radio" id="any" wire:model="category" class="accent-purple w-4 h-4" wire:click="category" value="0">
               <label for="any" class="capitalize">any</label>
           </li>
           @foreach ($categories as $category)
               <li class="flex items-center gap-2">
                    <input type="radio" id="classified" wire:model="category" class="accent-purple w-4 h-4" wire:click="category" value="{{ $category->id}}">
                    <label for="classified" class="capitalize">{{ __($category->name) }}</label>
                </li>
                @if(!is_null($category->subcategories))
                <ul class="mt-1 ml-5 flex flex-col gap-1">
                    @foreach ($category->subcategories as $subcategory)
                        <li class="flex items-center gap-2">
                            <input type="radio" id="apperal" wire:model="category" class="accent-purple w-4 h-4" wire:click="category" value="{{ $subcategory->id}}">
                            <label for="apperal" class="capitalize">{{ __($subcategory->name) }}</label> 
                        </li>
                    @endforeach
                </ul>
                @endif
           @endforeach
       </ul>
   </div>
   <div>
       <p class="capitalize font-semibold">price</p>
       <input type="number" wire:model="min_price" wire:model.live="min_price" id="min_price" placeholder="Min Price" class="mt-3 w-full border border-border-white rounded-md p-4 focus:outline-none focus:border-purple">
       @error('min_price')
            <p class="text-sm mt-1 text-red-500">{{ $message}}</p>
       @enderror
       <input type="number" wire:model="max_price" wire:model.live="max_price" id="max_price" placeholder="Max Price" class="mt-1 w-full border border-border-white rounded-md p-4 focus:outline-none focus:border-purple">
   </div>
</div>
<?php

namespace App\Livewire\Listings;

use Livewire\Component;
use App\Models\Category;
use Livewire\Attributes\Url;
use Livewire\Attributes\Validate;

class Sidebar extends Component
{
    #[Url]
    public $type = 'any';

    #[Url]
    public $category = 'any';

    #[Url, Validate]
    public $min_price = '';

    #[Url]
    public $max_price = '';

    public $categories;

    public function mount()
    {
        $this->categories = $this->getNestedCategories();
        
    }

    private function getNestedCategories($parentId = null)
    {
        $categories = Category::where('parent_id', $parentId)->get();
        
        foreach ($categories as $category) {
            $category->subcategories = $this->getNestedCategories($category->id);
        }
        
        return $categories; 
    }

    public function rules()
    {
        return [
            'min_price' => 'numeric|between:1,999999999',
            'max_price' => 'numeric|between:1,999999999',
        ];
    }

    public function render()
    {
        return view('livewire.listings.sidebar', [
            'categories' => $this->categories,
        ]);
    }
}

Category.php

<?php

namespace App\Models;

use App\Status;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Category extends Model
{
    use HasFactory;

    public function Listings(): HasMany
    {
        return $this->hasMany(Listing::class, 'category_id');
    }

    public function child(): HasMany
    {
        return $this->hasMany(Category::class, 'parent_id');
    }

    public function parent(): BelongsTo
    {
        return $this->belongsTo(Category::class, 'parent_id');
    }

    protected $casts = [
        'is_active' => Status::class,
    ];
}

问题原因

  1. 分类数据未实时加载:$categories仅在mount()方法初始化,价格更新触发组件重新渲染时,嵌套的subcategories数据因Livewire序列化丢失,无法正常展示。
  2. DOM元素ID重复:多个单选框使用相同的id(如classified、apperal),导致DOM解析异常,影响组件交互和渲染。

解决步骤

1. 修复Livewire组件数据加载逻辑

将分类数据改为计算属性,确保每次渲染都能重新获取完整的嵌套分类:

<?php

namespace App\Livewire\Listings;

use Livewire\Component;
use App\Models\Category;
use Livewire\Attributes\Url;
use Livewire\Attributes\Validate;
use Livewire\Attributes\Computed;

class Sidebar extends Component
{
    #[Url]
    public $type = 'any';

    #[Url]
    public $category = 'any';

    #[Url, Validate]
    public $min_price = '';

    #[Url]
    public $max_price = '';

    public function rules()
    {
        return [
            'min_price' => 'numeric|between:1,999999999',
            'max_price' => 'numeric|between:1,999999999',
        ];
    }

    #[Computed]
    public function categories()
    {
        return $this->getNestedCategories();
    }

    private function getNestedCategories($parentId = null)
    {
        $categories = Category::where('parent_id', $parentId)->get();
        
        foreach ($categories as $category) {
            $category->subcategories = $this->getNestedCategories($category->id);
        }
        
        return $categories; 
    }

    public function render()
    {
        return view('livewire.listings.sidebar');
    }
}

2. 修复Blade模板重复ID问题

给每个单选框设置唯一ID,确保与label一一对应:

<div class="bg-white p-6 border border-border-white rounded-md hidden md:flex md:flex-col md:gap-6 md:max-w-[270px] h-fit">
    <div>
       <p class="capitalize font-semibold">type</p>
       <ul class="mt-3 flex flex-col gap-1">
           <li class="flex items-center gap-2">
               <input type="radio" id="type-any" wire:model="type" class="accent-purple w-4 h-4" value="any">
               <label for="type-any" class="capitalize">any</label>
           </li>
           <li class="flex items-center gap-2">
               <input type="radio" id="type-auction" wire:model="type" class="accent-purple w-4 h-4" value="auction">
               <label for="type-auction" class="capitalize">Auction</label>
           </li>
           <li class="flex items-center gap-2">
               <input type="radio" id="type-classified" wire:model="type" class="accent-purple w-4 h-4" value="classified">
               <label for="type-classified" class="capitalize">classified</label>
           </li>
           selected {{$type}}
       </ul>
   </div> 
    <div>
       <p class="capitalize font-semibold">category</p>
       <ul class="mt-3 flex flex-col gap-1">
           <li class="flex items-center gap-2">
               <input type="radio" id="category-any" wire:model="category" class="accent-purple w-4 h-4" value="0">
               <label for="category-any" class="capitalize">any</label>
           </li>
           @foreach ($categories as $category)
               <li class="flex items-center gap-2">
                    <input type="radio" id="category-{{ $category->id }}" wire:model="category" class="accent-purple w-4 h-4" value="{{ $category->id}}">
                    <label for="category-{{ $category->id }}" class="capitalize">{{ __($category->name) }}</label>
                </li>
                @if($category->subcategories->isNotEmpty())
                <ul class="mt-1 ml-5 flex flex-col gap-1">
                    @foreach ($category->subcategories as $subcategory)
                        <li class="flex items-center gap-2">
                            <input type="radio" id="category-{{ $subcategory->id }}" wire:model="category" class="accent-purple w-4 h-4" value="{{ $subcategory->id}}">
                            <label for="category-{{ $subcategory->id }}" class="capitalize">{{ __($subcategory->name) }}</label> 
                        </li>
                    @endforeach
                </ul>
                @endif
           @endforeach
       </ul>
   </div>
   <div>
       <p class="capitalize font-semibold">price</p>
       <input type="number" wire:model.live="min_price" id="min_price" placeholder="Min Price" class="mt-3 w-full border border-border-white rounded-md p-4 focus:outline-none focus:border-purple">
       @error('min_price')
            <p class="text-sm mt-1 text-red-500">{{ $message}}</p>
       @enderror
       <input type="number" wire:model.live="max_price" id="max_price" placeholder="Max Price" class="mt-1 w-full border border-border-white rounded-md p-4 focus:outline-none focus:border-purple">
   </div>
</div>

3. 优化Category模型关联(可选)

定义subcategories关联,简化递归查询逻辑:

<?php

namespace App\Models;

use App\Status;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Category extends Model
{
    use HasFactory;

    public function listings(): HasMany
    {
        return $this->hasMany(Listing::class, 'category_id');
    }

    public function subcategories(): HasMany
    {
        return $this->hasMany(Category::class, 'parent_id');
    }

    public function parent(): BelongsTo
    {
        return $this->belongsTo(Category::class, 'parent_id');
    }

    protected $casts = [
        'is_active' => Status::class,
    ];
}

然后修改组件的getNestedCategories方法:

private function getNestedCategories($parentId = null)
{
    return Category::where('parent_id', $parentId)->with('subcategories')->get();
}

内容的提问来源于stack exchange,提问作者Dilhan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 17:45:53