Laravel Multi-level menu

You need a multi-level menu that will be output from the database. The structure is still like this. There is a common template:

<!DOCTYPE html>
<html lang="en">
<head>
  @yield('meta')
  @yield('staticlinks')
  @yield('pagescss')

<title>@yield('tittle')</title>
</head>
<body>
  @include('inc.header')
  @yield('catalog')
  @yield('content')
  @include('inc.footer')
  @yield('staticjs')
  @yield('pagesjs')
</body>
</html>

header it is located in a separate file and is connected via @include. The problem is that all the solutions I've seen on the web are made for single-page sites. They are added by the route ala Route::get('/', 'MainController@index');. And this is a regular template that has a link to it. I have on header, catalog and footer there is no link, these are pluggable, embedded views. And that's where I'm stuck. If I tried to do some perverted ways and wrote nonsense (as it seemed to me) like Route::get('catalog', 'MainController@catalog'); and in the cotroller some query from the database, then Laravel complained all the time that a variable was not defined in the template header.blade.php (for example, $catalog). Does anyone have any ideas on how to implement this multi-level menu?

P.S. so for reference, in the table menus fields id, name, url, parent_id and img.

Model Menu:

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');
}

I tried to make such a selection in the controller, but what should I return if there is no link to catalog:

public function catalog(){
$catalog= Menu::with('children')->where('parent_id','=',0)->get();
    return view('inc.catalog',['catalog'=>$catalog]);}

In the route, respectively: Route::get('catalog', 'MainController@catalog');

Well, in the view itself:

<div class='cssmenu'>
    <ul>
     <li class='has-sub'><a><span>Каталог</span></a>
      <ul>
        @foreach($catalog as $item)
         <li class='has-sub'><a href="/{{ $item->url }}"><img class="catalogimg" src="/img/jpg&png/categories/{{ $item->title}}"><span>{{ $item->name }}</span></a>
          <ul>
            @if($item->sub_menu->count() > 0)
            @foreach($menu->sub_menu as $sub)
             <li><a href="/{{ $sub->url }}"><img class="catalogimg" src="/img/jpg&png/categories/{{ $sub->img}}"><span>{{ $sub->title }}</span></a></li>
            @endforeach

          </ul>
            @endif
         </li>
         @endforeach
      </ul>
     </li>
    </ul>
  </div>
Author: Hi_TecH, 2020-09-24

2 answers

Half of all the menu items (catalog sections)

$items = Category::all();

And pass this variable to the template, say layout.part.tree (using 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

The template connects itself recursively, allowing you to implement a menu of any nesting level. Moreover, we use only one query to the database. And we connect this template where you need to show the catalog menu:

<div id="catalog-tree">
    @include('layout.part.tree', ['parent' => 0])
</div>
 0
Author: Евгений, 2020-11-01 06:23:58

Version Laravel 8. x (the solution is suitable for a two-level menu)
In general, break the entire Internet, taking some information from various sources, including from the docks, I found a simple and working method of how to get a multi-level menu out of the database(it works for a two-level one, I haven't tried it for more). I will explain as much as possible in detail, suddenly beginners will look.

Since I have a menu, header and footer are connected via @inlude make the standard method via the controller, passing a variable to the top did not work. Therefore, I found a way to do it differently on the internet, including the docks. laravel provides this functionality, called VIEW COMPOSERS (not to be confused with the package manager composer). First, we need to create a new provider with the following command:

php artisan make:provider ComposerServiceProvider

Add it immediately to the array providers, somewhere at the end, which is located at the address config\app.php:

    'providers' => [
         
         /*
         * Other Service Providers...
         */
        App\Providers\ComposerServiceProvider::class,
],

Next, there are 2 options, the first is to use pure колбэки, the second is use класс. For more information about both options, see:

Https://si-dev.com/ru/blog/laravel-view-composers.

I used a class. To do this, we need to create a folder in the App\Http directory ViewComposers and a file in it NavigationComposer.php. The names and location of the files are not as important as I realized, but I did as the authors of various articles advised. In the file NavigationComposer.php , we will register a selection from the database for our menu. I have it only two-level, so the sample looks like so:

    <?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);
      }
}

It is also necessary to make some adjustments to the previously created ComposerServiceProvider. It is located in app\Providers.And we prescribe it there:

    <?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'
        );
    }
}

I threw off the general structure, but you should only add what is inside the function boot. The first argument is our menu template (I have it in resourses\views\inc\catalog.blade.php), and the second one is just our navigator, where we make a selection. Just as naturally, we need a model and a migration. Creating them:

php artisan make:model -m Menu.

Then we find our the table in database\migrations. If you created it with the same name as me, it will be called датасозданияcreate_menus_table.php. It is enough to write in it:

Schema::create('menus', function (Blueprint $table) {
        $table->increments('id');
        $table->integer('parent_id')->unsigned()->nullable()->index();
        $table->string('name');
        $table->string('url');
    });

After that, you start the migration php artisan migrate. And fill out your sign. In the column parent_id for categories, enter, for example, NULL, and for subcategories id the category to which this subcategory belongs. Then open the model at app\Models\Menu (or your model with a different name). And we prescribe the dependency:

    <?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');
        }
}

And now it remains to output these the data is in our view. I did it as follows:

<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>

I AM NOT the author of these solutions, I took information from different sites and sources and chose what suited me (I specified this for reference, just in case). I hope I helped someone.

 0
Author: Hi_TecH, 2020-11-19 08:28:17