Многоуровневое меню Laravel
Тут был подробный вопрос о том как выводить меню из БД в header/footer и т.д., которые подключаются к основному шаблону через @inlude. Удалил из за ненадобности.
В комменте ниже, я описал, как я смог это сделать.
Ответы (2 шт):
Версия Laravel 8.x
В общем, перерыв весь инет, взяв некоторую инфу с разных источников в том числе и с доков, нашел не сложный и рабочий метод как вывести из БД многоуровневое меню. Объясню максимально подробно, вдруг будут смотреть новички.
Т.к. у меня меню, header и footer подключаются через @inlude сделать стандартным методом через контроллер, передав во вьху переменную, не получалось. Поэтому нашел на просторах сети, в том числе и доках способ как это сделать иначе. В laravel предусмотрен данный функционал, называется VIEW COMPOSERS (не путать с пакетным менеджером composer).
Для начала нам нужно создать новый провайдер следующей командой:
php artisan make:provider ComposerServiceProvider
Добавим его сразу в масив providers, где нибудь в конце, который находится по адресу config\app.php:
'providers' => [
/*
* Other Service Providers...
*/
App\Providers\ComposerServiceProvider::class,
],
Далее есть 2 варианта, первый использовать чисто колбэки, второй это использовать класс.
Подробно об обоих вариантах рассмотрено тут:
https://si-dev.com/ru/blog/laravel-view-composers.
Я использовал класс. Для этого нам нужно создать в директории App\Http папку ViewComposers и в ней файл NavigationComposer.php. Названия и локация файлов не так важна, как я понял, но я поступил как советовали авторы разных статей.
В файле NavigationComposer.php мы будем прописывать выборку из БД для нашей меню.
У меня она всего двухуровневая, поэтому выборка выглядит так:
<?php
namespace App\Http\ViewComposers;
use Illuminate\View\View;
use App\Models\Menu;
class NavigationComposer
{
public function compose(View $view)
{
$catalog = Menu::where('parent_id','=',NULL)->get();
return $view->with('catalog', $catalog);
}
}
Так же необходимо внести некоторые коррективы в ранее созданный нами ComposerServiceProvider. Он находится в app\Providers.И прописываем там:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\View;
use App\Models\Menu;
class ComposerServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
View::composer(
'inc.catalog', 'App\Http\ViewComposers\NavigationComposer'
);
}
}
Я скинул общую структуру, но добавить вы должны только то,что находится внутри функции boot. Первый аргумент это наш шаблон меню (он у меня находится в resourses\views\inc\catalog.blade.php), а второй этот как раз наш навигатор, где мы делаем выборку.
Так же естественно нам нужна модель и миграция.
Создаем их:
php artisan make:model -m Menu.
Потом находим нашу таблицу в database\migrations. Если вы ее создали с тем же названием что и я, то она будет называться датасозданияcreate_menus_table.php.
В ней достаточно прописать:
Schema::create('menus', function (Blueprint $table) {
$table->increments('id');
$table->integer('parent_id')->unsigned()->nullable()->index();
$table->string('name');
$table->string('url');
});
После запускаете миграцию php artisan migrate. И заполняете вашу табличку. В графе parent_id для категорий пропишите, например, NULL, а для подкатегорий id той категории, к которой данная подкатегория относится.
Дальше открываем модель по адресу app\Models\Menu(либо ваша модель с другим названием).
И прописываем зависимость:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Menu extends Model
{
use HasFactory;
protected $table = 'menus';
public function parent()
{
return $this->belongsTo('App\Models\Menu', 'parent_id');
}
public function children()
{
return $this->hasMany('App\Models\Menu', 'parent_id');
}
}
И теперь осталось вывести эти данные в нашей вьюехе. Делал я следующим образом:
<div class='cssmenu'>
<ul>
<li class='has-sub'><a><span>Каталог</span></a>
<ul>
@foreach($catalog as $cat) <!-- Здесь выводятся категории-->
<li class='has-sub'><a href="/{{ $cat->url }}"><img class="catalogimg" src="/img/jpg&png/categories/{{ $cat->img }}"><span>{{$cat->name}}</span></a>
<ul>
@foreach($cat->children as $subcat) <!-- Здесь подкатегории-->
<li><a href="/{{ $subcat->url }}"><img class="catalogimg" src="/img/jpg&png/categories/{{ $subcat->img }}"><span>{{ $subcat->name }}</span></a></li>
@endforeach
</ul>
</li>
@endforeach
</ul>
</li>
</ul>
</div>
Я НЕ ЯВЛЯЮСЬ автором данных решений, инфу брал с разных сайтов и источников и выбрал то, что подошло мне (это для справки уточнил, на всякий случай). Надеюсь кому-то помог.
Получем все элементы меню (разделы каталога)
$items = Category::all();
И передаем эту переменную в шаблон, скажем layout.part.tree (с помощью ComposerServiceProvider)
@if (count($items->where('parent_id', $parent)))
<ul>
@foreach ($items->where('parent_id', $parent) as $item)
<li>{{ $item->name }}
@include('layout.part.tree', ['parent' => $item->id])
</li>
@endforeach
</ul>
@endif
Шаблон подключает себя рекурсивно, позволяя реализовать меню любого уровня вложенности. Причем, мы используем только один запрос к БД. И подключаем этот шаблон там, где нужно показать меню каталога:
<div id="catalog-tree">
@include('layout.part.tree', ['parent' => 0])
</div>