papas-source
papas-source

Reputation: 1281

blade templates recursive includes

I have an array of items that represent the file and dir structure of a directory on the server.

The $items array is constructed like this:

Array
(
    [folder1] => Array
        (
            [folder1_1] => Array
                (
                    [0] => filenameX.txt
                    [1] => filenameY.txt
                )
        )
    [pages] => Array
        (
        )
    [0] => filename.txt
    [1] => filename1.txt
)

what we want, is essentially <ul> with <li> for every node.

the resulting HTML should be something like

Now, my question has to do with nested includes with laravel's blade templating engine.

I have a view list.blade.php with the following contents

<div class="listing">
  @include('submenu', array('items', $items))
</div>

and I pass it the array like this:

View::make('list')->with('items', $items)

the included template (submenu.blade.php) has the following:

<ul>
@foreach($items as $key=>$value)
    @if (is_array($value))
        <li>{{$key}}/
        @include('submenu', array('items', $value))
        </li>
    @else
        <li>{{$value}}</li>
    @endif
@endforeach
</ul>

I @include the same template from within itself but with the new data, in case the $value is an array (directory)

First of all, is this at all possible?

If not, is there another way to achive the desired result?

TIA,

Upvotes: 6

Views: 5872

Answers (2)

Silver Paladin
Silver Paladin

Reputation: 106

Instead of using array, use an eloquent collection. Instead of using @include, use \View::make. It cleans up the code a bit. Here is an example drop down menu for the Foundation 5 framework, using an eloquent model with parent/child relationship:

My model has a parent->child relationship

public function children() {
    return $this->hasMany('Category', 'parent_id');
}

I generate my results like so in my controller

$categories = \Category::where('parent_id', '=', '0')->with('children')->get();

Blade template: _partials.dd-menu.blade.php

<ul class="{{$class}}">
@foreach($items as $item)
<?php
$active = $item->id == \Input::get('category') ? 'active' : '';
$hdd = $item->children->count() ? 'has-dropdown' : '';
?>
<li class="{{$hdd}} {{$active}}">
    <a href="?category={{$item->id}}">{{$item->name}}</a>
    @if ($item->children->count())
    {{ View::make('_partials.dd-menu')->withItems($item->children)->withClass('dropdown')}}
    @endif
</li>
@endforeach

In your parent blade:

<nav class="top-bar" data-topbar role="navigation">
            <ul class="title-area">
                <li class="name">
                    <h1><a href="?category=">Categories</a></h1>
                </li>
                <!-- Remove the class "menu-icon" to get rid of menu icon. Take out "Menu" to just have icon alone -->
                <li class="toggle-topbar menu-icon"><a href="#"><span>Menu</span></a></li>
            </ul>
            <section class="top-bar-section">
                <!-- Right Nav Section -->
                {{ View::make('_partials.dd-menu')->withItems($categories)->withClass('right')}}
            </section>
        </nav>

Upvotes: 0

duellsy
duellsy

Reputation: 8595

Yes, this is indeed possible.

However, there's an issue in your includes, you have:

@include('submenu', array('items', $value))

It should be:

@include('submenu', array('items' => $value))

It's worth noting also another hidden blade statment, @each. You can use this instead of looping through the array yourself, something like this:

<ul>
    @each('item.detail', $items, 'item')
</ul>

Then you create a new blade file named item.detail and pop what you previously had in your loop in that file. It helps to clean up your view from having more and more nested loops.

The data for the item when you are inside your new blade file will be held in the third parameter, in this case $item

Upvotes: 8

Related Questions