the model
class Menu extends Eloquent {
public static $table = 'menus';
public function parent_menu()
{
return $this->belongs_to('Menu', 'parent_id');
}
}
how I get it in the controller:
$menus = Menu::with('parent_menu')->get();
how do I render it in the view:
foreach($menus as $m)
{
echo $m->parent_menu->title;
}
looks like there is a problem when the relation is inside a table, i get an error
`trying to get property of non object`
is there a solution for this?
I have implemented a way to get endless depth in menu's in Laravel 4. It is not exactly what you ask, but the technique should be easily adaptable.
For starters my menu is just an array (for now) that gets assigned to the master view and that 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 could easily achieve this structure by using a Model as well. You will need function child_items
or something as we will render the menu from the top down, and not from the bottom up.
Now in my master 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 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 $item
variable. This means can go as deep as you want in your menu structure. (The php block is just there to prepare some variables so I could keep the actual template code clean and readable, technically it is not required).
I stripped the Twitter Bootstrap code in the snippets above to keep things simple (I actually have titles, dropdown toggles, icons, dividers, ... in my template / array), so the code is not tested. The full version is working fine for me though, so let me know if I made a mistake somewhere.
Hope this helps you (or anyone else, cause this is a rather old question) on the way. Let me know if you need any more pointers / help, or if you want my full code.
Happy coding!
I believe the following is the correct way to do recursion in laravel.
Supposing we have a children relation, 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 will get all the children records recursively, and you can access them like this:
$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 not tested, the following might be useful to flatten all the recursive relations into one collection of models (check the documentation on adding new methods to collections):
// 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's 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;
}
}
That way 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 ) {
}
My laravel menu with unlimited submenus (menu items from 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 the BaseController or where you want, put
$navitems=DB::table('navigations')->get();
$menu=BaseController::CreateMenu(0,$navitems,0);
return View::share($menu);
After that, i put the menu html to the macro.php
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 templating) just call the
{{ HTML::MakeNavigation($menu) }}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With