Laravel menu self-regulation

model

class Menu extends Eloquent { public static $table = 'menus'; public function parent_menu() { return $this->belongs_to('Menu', 'parent_id'); } } 

how do i get it in the controller:

 $menus = Menu::with('parent_menu')->get(); 

how to make it in view:

 foreach($menus as $m) { echo $m->parent_menu->title; } 

it looks like there is a problem when the relation is inside the table, I get an error

 `trying to get property of non object` 

is there any solution for this?

+6
source share
4 answers

I have implemented a way to get infinite depth in the menu in Laravel 4. This is not quite what you are asking for, but the technique should be easily adapted.

To start, my menu is just an array (at the moment), which is assigned to the main view and looks something like this.

 $menu = array( array( 'name' => 'item1', 'url' => '/' ), array( 'name' => 'item2', 'url' => '/', 'items' => array( array( 'name' => 'subitem1', 'url' => '/' ), array( 'name' => 'subitem2', 'url' => '/' ) ) ) ); 

You can easily achieve this structure using the model as well. You will need the child_items function or something, since we will display the menu from top to bottom, and not from bottom to top.

Now in my main blade template, I do this:

 <ul> @foreach ($menu as $item) @include('layouts._menuItem', array('item' => $mainNavItem)) @endforeach </ul> 

And then in the layouts._menuItem template layouts._menuItem I do this:

 <?php $items = array_key_exists('items', $item) ? $item['items'] : false; $name = array_key_exists('name', $item) ? $item['name'] : ''; $url = array_key_exists('url', $item) ? url($item['url']) : '#'; $active = array_key_exists('url', $item) ? Request::is($item['url'].'/*') : false; ?> <li class="@if ($active) active @endif"> <a href="{{ $url }}">{{ $name }}</a> @if ($items) <ul> @foreach ($items as $item) @include('layouts._menuItem', array('item' => $item)) @endforeach </ul> @endif </li> 

As you can see, this template calls itself recursively, but with a different variable $item . This means that you can do as deep as you want in the structure of your menu. (The PHP block is designed to prepare some variables, so I could keep the actual code of the template clean and readable, technically it is not required).

I removed the Bootstrap Twitter code in the above snippets to make things simple (I actually have headers, dropdown buttons, icons, separators, ... in my template / array), so the code is not checked. The full version works fine for me, so let me know if I made a mistake somewhere.

Hope this helps you (or anyone else, because it's a pretty old question) along the way. Let me know if you need additional pointers / help or if you want to get the full code.

Happy coding!

+4
source

I believe the following is the right way to do recursion in laravel.

Suppose we have a child relationship, you can add this to your model class:

 public function getDescendants ( $parent= false ) { $parent = $parent ?: $this; $children = $parent->children()->get(); foreach ( $children as $child ) { $child->setRelation( 'children', getDescendants( $child ) ); } return $children; } 

The above recursive details will be written to all records, and you can access them as follows:

 $d = Category::find(1)->getDescendants(); foreach ( $d as $child_level_1 ) { foreach ( $child_level_1->children as $child_level_2 ) { foreach ( $child_level_2->children as $child_level_3 ) { // ...... this can go on for infinite levels } } } 

Although this has not been tested, it can be useful to smooth out all recursive relationships into one set of models ( check the documentation on adding new methods to the collection ):

 // Add this to your model public function newCollection ( array $models = array() ) { return new CustomCollection( $models ); } // Create a new file that extends the orginal collection // and add the flattenRelation method class CustomCollection extends Illuminate\Database\Eloquent\Collection { // Flatten recursive model relations public static function flattenRelation ( $relation ) { $collection = $this; // Loop through the collection models foreach ( $collection as $model ) { // If the relation exists if ( isset($model->relations[$relation]) ) { // Get it $sub_collection = $model->relations[$relation]; // And merge it items with the original collection $collection = $collection->merge( $sub_collection->flatten($relation) ); // Them remove it from the relations unset( $model->relations[$relation] ); } } // Return the flattened collection return $collection; } } 

So you can do the following:

 // This will get the descenands and flatten them recursively $d = Category::find(1)->getDescendants()->flattenRelation( 'children' ); // This will give you a flat collection of all the descendants foreach ( $d as $model ) { } 
+2
source

My laravel menu with unlimited submenus (menu items from the database)

 public function CreateMenu( $parid, $menu, $level ) { $output = array(); $action= Route::current()->getUri(); $uri_segments = explode('/', $action); $count=count($uri_segments); foreach( $menu as $item => $data ) { if ($data->parent_id == $parid) { $uri=''; $output[ $data->id ] = $data; for($i=0; $i<=$level; $i++) { if($i < $count) { $uri.="/".Request::segment($i+1); } if($uri == $data->link ) { $output[ $data->id ]->activeClass = 'active'; $output[ $data->id ]->inClass = 'in'; } else { $output[ $data->id ]->activeClass = ''; $output[ $data->id ]->inClass = ''; } $output[ $data->id ]->level = $level+2; } $output[ $data->id ]->submenu = self::CreateMenu( $data->id, $menu, $level+1 ); } } return $output; } 

In BaseController or wherever you want, put

 $navitems=DB::table('navigations')->get(); $menu=BaseController::CreateMenu(0,$navitems,0); return View::share($menu); 

After that I put the html menu in the macro.php file

 HTML::macro('MakeNavigation', function($data) { foreach ($data as $key => $value) { if($value->submenu) { echo '<li class="'.$value->activeClass.'"> <a href="'.$value->link.'" class="'.$value->activeClass.'">"' .$value->name.' <span class="fa arrow"></span> </a>'; echo "<ul class='nav nav-".$value->level."-level ".$value->inClass." '>"; HTML::MakeNavigation($value->submenu); echo "</ul>"; } else { echo '<li class="'.$value->activeClass.'"> <a href="'.$value->link.'" class="'.$value->activeClass.'">' .$value->name.' </a>'; } echo "</li>"; }}); 

And in the view (blade templates) just call

 {{ HTML::MakeNavigation($menu) }} 
+1
source

This can help. Here's how I did it with product categories / subcategories

Model:

 <?php class Category extends Eloquent { protected $table = 'product_category'; public function subcat() { return $this->hasMany('Category', 'node_id')->orderBy('position'); } 

Request (you can use conditions when using active download)

 $categories = Category::with(['subcat' => function($query){ $query->with(['subcat' => function($query){ $query->orderBy('name'); }]) }])->where('node_id', 0)->orderBy('position')->get(['id', 'name']); foreach ($categories as $level1) { echo $level1->name; foreach ($level1->subcat as $level2) { echo $level2->name; foreach ($level2->subcat as $level3) { echo $level3->name; } } } 
0
source

Source: https://habr.com/ru/post/944292/


All Articles