EDIT: Based on the advice by Tom, here's my modified code. Works well, so thanks Tom.
<?php
$categories = Category::where('parent_id', '0')->get();
foreach($categories as $category):
$category_courses = get_courses($category->id);
$sub_categories = Category::where('parent_id', $category->id)->get();
$max_iteration = ceil(count($sub_categories) / 4);
?>
<li class="dropdown mega-menu-4">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" title="{{ $category->name }}">{{ $category->name }} <b class="caret"></b></a>
<ul class="dropdown-menu style-plain">
<li class="one-column">
<?php foreach($sub_categories as $key=>$sub_category): ?>
<ul>
<li class="nav-title">{{ $sub_category->name }}</li>
<?php foreach(get_courses($sub_category->id) as $course): ?>
<li>{{ $course->title }}</li>
<?php endforeach; ?>
</ul>
<?php if(($key + 1) % $max_iteration == 0): ?>
</li>
<li class="one-column">
<?php endif; ?>
<?php endforeach; ?>
</li>
</ul>
</li>
<?php endforeach; ?>
Though now I'm thinking I should create a tree in memory after getting some advice from machuga in the #laravel IRC. Essentially based on this SO answer Flat PHP Array to Hierarchy Tree.
I'm trying to create a mega menu in my Laravel app with the following structure, so that it works well with Bootstrap 2.3.
<div class="navbar">
<div class="navbar-inner">
<div class="container">
<div class="nav-collapse collapse">
<ul class="nav">
<li class="dropdown mega-menu-4 transition">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">$category <b class="caret"></b></a>
<ul class="dropdown-menu">
<li class="one-column">
<ul>
<li class="nav-title">$subCategory 1</li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
</ul>
<ul>
<li class="nav-title">$subCategory 1</li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
</ul>
</li>
<li class="one-column">
<ul>
<li class="nav-title">$subCategory 2</li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
</ul>
<ul>
<li class="nav-title">$subCategory 2</li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
</ul>
</li>
<li class="one-column">
<ul>
<li class="nav-title">$subCategory 3</li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
</ul>
<ul>
<li class="nav-title">$subCategory 3</li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
</ul>
</li>
<li class="one-column">
<ul>
<li class="nav-title">$subCategory 4</li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
</ul>
<ul>
<li class="nav-title">$subCategory 4</li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
<li><a href="#">$course</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</div>
In my database I have the following:
table: categories
fields: id, parent_id, name, slug
table: courses
fields: id, category_id, title, slug
I am struggling with the for and foreach loop logic in order to generate the ul elements within the li:one-column elements. I want a maximum of 4 columns, with as many rows as necessary, depending on how many sub-categories there are.
So, in the categories table a top level category will not have a 'parent_id' assigned to it but a sub-category will have a top level 'parent_id' assigned to it.
So I can foreach through $categories as $category and setup the top level menu structure which will create a li:dropdown element for each top level category. The problem comes when trying to implement the li:one-column elements. Each li:one-column element is essentially a column in the mega menu, so I need a maximum of four. Each ul within the column is a sub-category.
So if there are 13 sub-categories under the top level category, each column will have the following:
Column 1: 4
Column 2: 3
Column 3: 3
Column 4: 3
Then if a new sub-category were added, it would be:
Column 1: 4
Column 2: 4
Column 3: 3
Column 4: 3
And so on. So essentially each new sub-category would fill up the next available column.
There's probably a really easy solution to this but I'm struggling with the logic at the minute. Thanks in advance.
EDIT: I'm getting closer using:
<ul class="nav">
@foreach($categories as $category)
<?php
$category_courses = get_courses($category->id);
$sub_categories = Category::where('parent_id', $category->id)->get();
$sub_categories_total = count($sub_categories); // int(5)
$sub_category_split = round($sub_categories_total / 4); // int(1)
?>
<li class="dropdown mega-menu-4">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" title="{{ $category->name }}">{{ $category->name }} <b class="caret"></b></a>
<ul class="dropdown-menu style-plain">
@for($i=0; ++$i <=4;)
<li class="one-column">
@foreach($sub_categories as $sub_category)
<ul >
<li class="nav-title">{{ $sub_category->name }}</li>
@foreach(get_courses($sub_category->id) as $course)
<li>{{ $course->title }}</li>
@endforeach
</ul>
@endforeach
</li>
@endfor
</ul>
</li>
@endforeach
</ul>
I'm still not quite there though, as it adds all sub_categories to each li:one-column element. I feel I'm certainly closer to a solution though.
EDIT: OK, I'm a lot closer now. I'm now able to output only the maximum iteration in each li:one-column element but it outputs exactly the same data. I need a way to continue the foreach loop from where it breaks.
<ul class="nav">
@foreach($categories as $category)
<?php
$category_courses = get_courses($category->id);
$sub_categories = Category::where('parent_id', $category->id)->get();
$max_iteration = round(count($sub_categories) / 4);
?>
<li class="dropdown mega-menu-4">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" title="{{ $category->name }}">{{ $category->name }} <b class="caret"></b></a>
<ul class="dropdown-menu style-plain">
@for($ci=0; ++$ci <=4;)
<li class="one-column">
@foreach($sub_categories as $key=>$sub_category)
@if($key <= $max_iteration)
<ul >
<li class="nav-title">{{ $sub_category->name }}</li>
@foreach(get_courses($sub_category->id) as $course)
<li>{{ $course->title }}</li>
@endforeach
</ul>
@else
<?php break; ?>
@endif
@endforeach
</li>
@endfor
</ul>
</li>
@endforeach
</ul>
EDIT: OK, this is the closest I've got so far and it's almost there. Still not right though, as it outputs 1 subcategory in the first column, column 2, column 3 but 2 in column 4. :-(
@foreach($categories as $category)
<?php
$category_courses = get_courses($category->id);
$sub_categories = Category::where('parent_id', $category->id)->get();
$max_iteration = round(count($sub_categories) / 4) + 1;
?>
<li class="dropdown mega-menu-4">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" title="{{ $category->name }}">{{ $category->name }} <b class="caret"></b></a>
<ul class="dropdown-menu style-plain">
<li class="one-column">
@foreach($sub_categories as $key=>$sub_category)
<ul>
<li class="nav-title">{{ $sub_category->name }}</li>
@foreach(get_courses($sub_category->id) as $course)
<li>{{ $course->title }}</li>
@endforeach
</ul>
@if($key <= $max_iteration)
</li>
<li class="one-column">
@endif
@endforeach
</li>
</ul>
</li>
@endforeach
You need to make 2 changes to achieve this. Firstly, you might need to use ceil
instead of round
for your $max_iteration
variable. This will ensure your columns always split in a consistent place.
Then, change this line...
@if($key <= $max_iteration)
to
@if(($key + 1) % $max_iteration == 0)
$key is a key starting at zero, so you need to increment it, as we're dealing with quantities starting at 1. Then, by checking its remainder when divided by $max_iteration
, we can tell if we've reached the next column. It will be zero when the key is a multiple of max iteration.