【问题标题】:Laravel Menu self recursionLaravel 菜单自递归
【发布时间】:2013-05-04 22:04:13
【问题描述】:

模型

 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`

有解决办法吗?

【问题讨论】:

  • 看来我需要在这种情况下使用数据透视表
  • 您确定要获取的菜单项有父项吗? “试图获取非对象的属性”表明没有可用的parent_menu
  • 在这里为来自搜索heera.it/laravel-model-relationship的人发布此信息

标签: php laravel laravel-4


【解决方案1】:

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

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

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

您也可以使用模型轻松实现此结构。您将需要函数 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 块只是用来准备一些变量,所以我可以保持实际模板代码的清洁和可读性,从技术上讲它不是必需的)。

为了简单起见,我在上面的 sn-ps 中剥离了 Twitter Bootstrap 代码(我的模板/数组中实际上有标题、下拉切换、图标、分隔线……),所以代码没有经过测试。不过,完整版对我来说工作正常,所以如果我在某个地方犯了错误,请告诉我。

希望这对您(或其他任何人,因为这是一个相当古老的问题)有所帮助。如果您需要更多指针/帮助,或者想要我的完整代码,请告诉我。

编码愉快!

【讨论】:

    【解决方案2】:

    我相信以下是在 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
            }
        }
    }
    

    虽然未经测试,但以下内容可能有助于将所有递归关系扁平化为一组模型 (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;
        }
    }
    

    这样您可以执行以下操作:

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

    【讨论】:

      【解决方案3】:

      我的 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) }}
      

      【讨论】:

        【解决方案4】:

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

        型号:

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

        【讨论】:

        • 但这对关卡来说是有限的,我想让它在深度上不受限制
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-04-05
        • 2018-08-28
        • 2013-11-05
        • 2013-06-13
        • 2015-04-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多