Laravel 菜单自递归
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16379416/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Laravel Menu self recursion
提问by Hello
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?
有解决方案吗?
回答by Pevara
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.
我已经在 Laravel 4 中实现了一种在菜单中获得无限深度的方法。这不是你所要求的,但该技术应该很容易适应。
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.
您也可以通过使用模型轻松实现此结构。您将需要函数child_items
或其他东西,因为我们将自上而下呈现菜单,而不是自下而上。
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:
然后在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>
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).
如您所见,此模板以递归方式调用自身,但使用不同的$item
变量。这意味着可以在菜单结构中尽可能深入。(php 块只是为了准备一些变量,所以我可以保持实际的模板代码干净和可读,从技术上讲,它不是必需的)。
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.
我删除了上面代码片段中的 Twitter Bootstrap 代码以保持简单(我的模板/数组中实际上有标题、下拉切换、图标、分隔符……),因此代码没有经过测试。不过,完整版对我来说很好用,所以如果我在某处犯了错误,请告诉我。
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!
快乐编码!
回答by ktsakas
I believe the following is the correct way to do recursion in laravel.
我相信以下是在 laravel 中进行递归的正确方法。
Supposing we have a children relation, you can add this to your model class:
假设我们有一个 children 关系,您可以将其添加到您的模型类中:
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 ) {
}
回答by user3229469
My laravel menu with unlimited submenus (menu items from database)
我的 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;
}
In the BaseController or where you want, put
在 BaseController 或你想要的地方,把
$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.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) }}
回答by tuurbo
This may be of some help. Heres how i did it with product categories / sub categories
这可能会有所帮助。这是我如何处理产品类别/子类别
Model:
模型:
<?php
class Category extends Eloquent {
protected $table = 'product_category';
public function subcat()
{
return $this->hasMany('Category', 'node_id')->orderBy('position');
}
Query (you can use conditions when using eager loading)
查询(使用急切加载时可以使用条件)
$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;
}
}
}