Laravel菜单自递归



模型

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

如何在控制器中获得它:

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

如何在视图中渲染它:

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

看起来有问题,当关系是在一个表中,我得到一个错误

`trying to get property of non object`

有解决方案吗?

我在Laravel 4中实现了一种在菜单中获得无限深度的方法。这并不完全是您所要求的,但该技术应该易于适应。

对于初学者来说,我的菜单只是一个数组(目前)被分配给主视图,它看起来像这样。

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

您也可以通过使用Model轻松实现此结构。你将需要功能child_items或其他东西,因为我们将呈现菜单从上到下,而不是从下到上。

现在在我的主刀片模板中,我这样做:

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

然后在layouts._menuItem模板中我这样做:

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

正如你所看到的,这个模板递归地调用自己,但是使用不同的$item变量。这意味着你可以在你的菜单结构中随心所欲地深入。(php块只是在那里准备一些变量,所以我可以保持实际的模板代码干净和可读,技术上它是不需要的)。

我在上面的代码片段中剥离了Twitter Bootstrap代码,以保持事情简单(我实际上有标题,下拉开关,图标,分隔符,…在我的模板/数组中),所以代码没有被测试。完整版本对我来说工作得很好,所以让我知道如果我在某个地方犯了错误。

希望这对你(或其他人,因为这是一个相当老的问题)有帮助。让我知道如果你需要更多的指针/帮助,或者如果你想要我的完整的代码。

编码快乐!

我相信下面是在laravel中进行递归的正确方法。

假设我们有一个子关系,你可以把它添加到你的模型类:

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

上面的代码将递归地获取所有子记录,您可以这样访问它们:

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

虽然没有经过测试,但下面的代码对于将所有递归关系平铺到一个模型集合中可能是有用的(请查看有关向集合添加新方法的文档):

// 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 IlluminateDatabaseEloquentCollection {
    // 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;
    }
}

这样你就可以做以下事情:

// 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 ) {
}

包含无限子菜单(数据库中的菜单项)的laravel菜单

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

在BaseController或你想要的地方,放入

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

之后,我将菜单html放入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>";
}});

在视图(刀片模板)中调用

{{ HTML::MakeNavigation($menu) }}

这可能会有所帮助。以下是我如何处理产品类别/子类别

模型:

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

查询(在使用即时加载时可以使用条件)

$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;
        }
    }
}

最新更新