Дерево категорий Laravel
Last updated
Last updated
Сидинг БД таблицы категорий
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->integer('p_id');
$table->timestamps();
});
}
Categories::factory()->create([
'id' => 1,
'name' => 'Главная',
'p_id' => 0,
]);
Categories::factory()->create([
'id' => 2,
'name' => 'Компьютеры',
'p_id' => 1,
]);
Categories::factory()->create([
'id' => 3,
'name' => 'Телефоны',
'p_id' => 1,
]);
Categories::factory()->create([
'id' => 4,
'name' => 'Планшеты',
'p_id' => 1,
]);
Categories::factory()->create([
'id' => 5,
'name' => 'Ноутбуки',
'p_id' => 2,
]);
Categories::factory()->create([
'id' => 6,
'name' => 'ПК',
'p_id' => 2,
]);
Categories::factory()->create([
'id' => 7,
'name' => 'Lenovo',
'p_id' => 5,
]);
Categories::factory()->create([
'id' => 8,
'name' => 'Apple',
'p_id' => 5,
]);
Categories::factory()->create([
'id' => 9,
'name' => 'Asus',
'p_id' => 5,
]);
Categories::factory()->create([
'id' => 10,
'name' => 'Acer',
'p_id' => 5,
]);
Categories::factory()->create([
'id' => 11,
'name' => 'Apple',
'p_id' => 3,
]);
Categories::factory()->create([
'id' => 12,
'name' => 'Samsung',
'p_id' => 3,
]);
Categories::factory()->create([
'id' => 13,
'name' => 'Nokia',
'p_id' => 3,
]);
Categories::factory()->create([
'id' => 14,
'name' => 'Huawei',
'p_id' => 3,
]);
Categories::factory()->create([
'id' => 15,
'name' => 'Xiaomi',
'p_id' => 3,
]);
Categories::factory()->create([
'id' => 16,
'name' => 'Apple',
'p_id' => 4,
]);
Categories::factory()->create([
'id' => 17,
'name' => 'Xiaomi',
'p_id' => 4,
]);
Categories::factory()->create([
'id' => 18,
'name' => 'Apple',
'p_id' => 6,
]);
Модель category
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Categories extends Model
{
use HasFactory;
public static function getCategories(){
// Получаем одним запросом все разделы
$arr = self::orderBy('name')->get();
// Запускаем рекурсивную постройку дерева и отдаем на выдачу
return self::buildTree($arr, 0);
}
public static function buildTree($arr, $pid = 0) {
// Находим всех детей раздела
$found = $arr->filter(function($item) use ($pid){return $item->p_id == $pid; });
// Каждому детю запускаем поиск его детей и записываем в свойство sub
foreach ($found as $key => $cat) {
$sub = self::buildTree($arr, $cat->id);
// То что ниже можно заменить на - $cat->sub = $sub;
$found[$key]->sub = $sub;
}
return $found;
}
// Альтернативный вариант
public static function getCategories2(){ // получаем корневые категории (у которых нету родителей) с жадной загрузкой отношения categories()
return self::where('p_id','=',0)->with('categories')->get();
}
public function categories(){ // отношение категории к дочерним категориям, отдаётся сразу с жадной загрузкой
return $this->hasMany(self::class,'p_id','id')->with('categories');
}
}
Контроллер
namespace App\Http\Controllers;
use App\Models\Categories;
use Illuminate\Http\Request;
class TreeController extends Controller
{
public function index(){
$categories = Categories::getCategories();
return view('tree')->with(['categories' => $categories]);
}
public function index2(){
$categories = Categories::getCategories2();
return view('tree2')->with(['categories' => $categories]);
}
}
Первый вариант
<ul>
@foreach($categories as $category)
<li>
{{$category->name}}
@if($category->sub->count())
@include('tree', ['categories' => $category->sub])
@endif
</li>
@endforeach
</ul>
Второй вариант (предпочтительный) через жадную загрузку отношений самих на себя
<ul>
@foreach($categories as $category)
<li>
{{$category->name}}
@if($category->categories->isNotEmpty())
@include('tree2',['categories' => $category->categories])
@endif
</li>
@endforeach
</ul>