Довольно часто в интернет-магазинах можно увидеть множество уровней категорий и подкатегорий, вплоть до бесконечности. Эта статья покажет вам, как сделать это элегантно при помощи Laravel Eloquent.
Мы создадим мини-проект для просмотра подкатегорий в детском магазине, например, пять уровней:
Миграция для БД
Простая схема для таблицы:
Schema::create('categories', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('name'); $table->unsignedBigInteger('category_id')->nullable(); $table->foreign('category_id')->references('id')->on('categories'); $table->timestamps(); });
У нас просто есть поле с названием и связь с самой таблицей. Таким образом, у большинства родительских категорий будет category_id = NULL, а у других подкатегории — свой собственный parent_id.
Вот наши данные в базе данных:
Модель Eloquent и отношения
Для начала, в app/Category.php мы добавляем простой метод hasMany() — у категории могут быть другие подкатегории:
class Category extends Model { public function categories() { return $this->hasMany(Category::class); } }
Теперь время главного «секрета» статьи . Знаете ли вы, что можно создавать рекурсивные отношения? Вот так:
public function childrenCategories() { return $this->hasMany(Category::class)->with('categories'); }
Таким образом, если вы вызовете Category::with(‘categories’), это получите один «дочерний» уровень, а Category::with(‘childrenCategories’) выдаст вам столько уровней, сколько сможет найти.
Маршрут и метод контроллера
Теперь давайте попробуем показать все категории и подкатегории, как в примере выше.
В routes/web.php добавим:
Route::get('categories', 'CategoryController@index');
А в app/Http/CategoryController.php сделаем так:
public function index() { $categories = Category::whereNull('category_id') ->with('childrenCategories') ->get(); return view('categories', compact('categories')); }
Как видите, мы загружаем только родительские категории, а дочерние как отношения. Всё просто.
Шаблон и рекурсивный суб-шаблон
Перейдем к структуре шаблонов. Так выглядит resources/views/categories.blade.php:
<ul> @foreach ($categories as $category) <li>{{ $category->name }}</li> <ul> @foreach ($category->childrenCategories as $childCategory) @include('child_category', ['child_category' => $childCategory]) @endforeach </ul> @endforeach </ul>
Сначала основные категории, а затем загружаем дочерние категории при помощи @include.
Самое приятное, что resources/views/admin/child_category.blade.php будет использовать рекурсивную загрузку самого себя. Посмотрите код:
<li>{{ $child_category->name }}</li> @if ($child_category->categories) <ul> @foreach ($child_category->categories as $childCategory) @include('child_category', ['child_category' => $childCategory]) @endforeach </ul> @endif
Как видите, внутри child_category.blade.php у нас есть @include(‘child_category’), поэтому шаблон рекурсивно загружает дочерние элементы, пока есть категории внутри текущей дочерней категории.
Вот и всё! Теперь у нас есть неограниченный уровень подкатегорий — в базе данных, в отношениях Eloquent и в шаблонах.
Автор: Povilas Korop
Перевод: Алексей Широков
Наш Телеграм-канал — следите за новостями о Laravel.