Build dynamic Mega Menu using recursion? - php

I'm trying to create a Mega Menu using PHP and I'm having a problem getting the structure to output correctly. I've hard-coded the Mega Menu to test everything and it works fine, but obviously I need PHP to create it for me.
I have an example with the Mega Menu hard-coded so everyone can see what I'm trying to create:
http://www.libertyeaglearms.com/dev
Or here's the code:
DESIRED OUTPUT:
<div id="wrapper">
<ul class="mega-menu">
<li class="mega-menu-drop">
Firearms
<div class="mega-menu-content">
<div class="column">
<h4>Rifles</h4>
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
<li>Four</li>
<li>Five</li>
</ul>
</div>
<div class="column">
<h4>Handguns</h4>
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
<li>Four</li>
<li>Five</li>
</ul>
</div>
<div class="column">
<h4>Shotguns</h4>
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
<li>Four</li>
<li>Five</li>
</ul>
</div>
</div>
</li>
<li class="mega-menu-drop">
Archery
<div class="mega-menu-content">
<div class="column">
<h4>Bows</h4>
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
<li>Four</li>
<li>Five</li>
</ul>
</div>
<div class="column">
<h4>Arrows</h4>
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
<li>Four</li>
<li>Five</li>
</ul>
</div>
</div>
</li>
</ul>
</div>
CURRENT OUTPUT: (very messed up. be warned, lol)
<div id="wrapper">
<ul class="mega-menu">
<li class="mega-menu-drop">
archery
<div class="mega-menu-content">
<div class="column">
<h4>compound</h4>
<ul>
<h4>bows</h4>
<ul>
</div>
</li>
<li class="mega-menu-drop">
firearms
<div class="mega-menu-content">
<div class="column">
<h4>rifle ammunition</h4>
<ul>
<h4>ammunition</h4>
<ul>
</div>
</li>
</ul>
</div>
HERE MY PHP:
$json = json_decode($category->buildDepartments(NULL),true);
function buildDepartments($array,$parent)
{
$html = '';
foreach($array as $category)
{
if($category['parent'] == $parent)
{
if($category['parent'] == NULL)
{
$html .= '<li class="mega-menu-drop">' . "\n\t\t\t\t";
$html .= ''.$category['category_name'].'' . "\n\t\t\t\t";
$html .= '<div class="mega-menu-content">' . "\n\t\t\t\t\t";
$html .= '<div class="column">' . "\n\t\t\t\t\t\t";
$html .= buildDepartments($array,$category['category_id']);
$html .= '</div>' . "\n\t\t\t";
$html .= '</li>' . "\n\t\t\t";
}
else
{
$html .= buildDepartments($array,$category['category_id']);
$html .= '<h4>'.$category['category_name'].'</h4>' . "\n\t\t\t\t\t\t";
$html .= '<ul>' . "\n\t\t\t\t";
}
}
}
return $html;
}
print(buildDepartments($json,NULL));
HERE'S MY DATABSE:
EDIT AFTER BOUNTY
Building off what icktoofay suggested, I cannot seem to figure out the foreach() loops. The problem I'm getting is I get the department name inside the mega menu when I should see category and sub categories. I think the problem is I need to perform a loop with a specific parent id in order to get all the children, but I'm not really sure if that's the problem. Here's the code:
<div id="wrapper">
<ul class="mega-menu">
<?php $json = json_decode($category->buildDepartments(NULL)); ?>
<?php foreach($json as $category): ?>
<li class="mega-menu-drop">
<?php if($category->parent === NULL){echo $category->category_name;} ?>
<div class="mega-menu-content">
<?php foreach($category as $subcategory): ?>
<div class="column">
<?php if($category->category_id === $category->parent){echo '<h4>' . $category->category_name . '</h4>';}?>
<ul>
<?php foreach($category as $subcategory)
{
echo '<li>' . $category->category_name . '</li>';
}
?>
</ul>
</div>
<?php endforeach; ?>
</div>
</li>
<?php endforeach; ?>
</ul>
</div>

Since it's a fixed depth and the format for each level is different, recursion may not be appropriate. Additionally, putting everything in strings is inelegant. Instead, you may want to try weaving the HTML and loops together like this:
<div id="wrapper">
<ul class="mega-menu">
<?php foreach($categories as $category): ?>
<li class="mega-menu-drop">
<?php echo $category->name; ?>
<div class="mega-menu-content">
<?php foreach($category->subcategories as $subcategory): ?>
<!-- and so on -->
<?php endforeach; ?>
</div>
</li>
<?php endforeach; ?>
</ul>
</div>
The code I've used here assumes you've already got it from a sort-of-flat-array to a tree-like structure. For example, if we've got a Category class:
class Category {
public $id;
public $parentID;
public $name;
public $parent;
public $subcategories;
public function __construct($id, $parentID, $name) {
$this->id = $id;
$this->parentID = $parentID;
$this->name = $name;
$this->parent = null; // will be filled in later
$this->subcategories = array(); // will be filled in later
}
}
And an array of associative arrays like you might get from a database call (which we'll call $flatCategories), we can build a bunch of not-yet-connected Category instances like this:
$categories = array();
foreach($flatCategories as $flatCategory) {
$categories[$flatCategory['id']] =
new Category($flatCategory['category_id'],
$flatCategory['parent'],
$flatCategory['category_name']);
}
Then we can connect them all up into a hierarchal structure:
foreach($categories as $category) {
if($category->parentID !== null) {
$category->parent = $categories[$category->parentID];
$category->parent->subcategories[] = $category;
}
}
Now they're all linked up and we only care about keeping references to the roots:
$roots = array();
// This could, of course, be merged into the last loop,
// but I didn't for clarity.
foreach($categories as $category) {
if($category->parentID === null) {
$roots[] = $category;
}
}
Now $roots contains all the root categories, and it's fairly simple to traverse. In my example at the top, I was assuming $categories had something similar to $roots in it.

you need to generate something called a super tree and use a recursive function to echo out each branch, this will allow for an unlimited depth tree, but, it also allows for an unknown depth tree.
First, SQL:
SELECT category_id, parent, category_name
Second, use SQL to create tree:
$super_tree = array();
while(($row = mysql_fetch_assoc($res)) != NULL) {
$parent = $row['parent'] == NULL ? 0 : $row['parent']; //clean up the parent
$super_tree[$parent][$row['category_id']] = $row['category_name'];
}
Third, echo out the tree
function echo_branch($tree_branch, $tree_root) {
echo("<ul>\n"); //open up our list for this branch
foreach($tree_branch as $id => $name) { //foreach leaf on the branch
echo("<li>"); //create a list item
echo("{$name}"); //echo out our link
if(!empty($tree_root[$id])) { //if our branch has any sub branches, echo those now
echo_branch($tree_root[$id], $tree_root); //pass the new branch, plus the root
}
echo("</li>\n"); //close off this item
}
echo("</ul>\n"); //close off this list
}
echo_branch($super_tree[0], $super_tree); //echo out unlimited depth tree structure
this allows for unlimited depth in your tree structure and allows for simple code to be able to create the structure within the code base.
All that you would need to do now is add in your extra's such as classes and your extra html elements in the correct places.
if you are looking to track the current depth of the tree to be able to echo different things depending on the depth, you can make the following alterations
In the function definition
function echo_branch($tree_branch, $tree_root, $depth) {
In the recursive call within the if
echo_branch($tree_root[$id], $tree_root, $depth++);
In the initial call
echo_branch($super_tree[0], $super_tree, 0);

Look like you are using the variable
$category
when you should use
$subcategory
Try this:
<div id="wrapper">
<ul class="mega-menu">
<?php $json = json_decode($category->buildDepartments(NULL)); ?>
<?php foreach($json as $category): ?>
<li class="mega-menu-drop">
<?php if($category->parent === NULL){echo $category->category_name;} ?>
<div class="mega-menu-content">
<?php foreach($category as $subcategory): ?>
<div class="column">
<?php if($subcategory->category_id === $category->parent){echo '<h4>' . $category->category_name . '</h4>';}?>
<ul>
<?php foreach($subcategory as $subcategory2)
{
echo '<li>' . $subcategory2->category_name . '</li>';
}
?>
</ul>
</div>
<?php endforeach; ?>
</div>
</li>
<?php endforeach; ?>
</ul>
</div>

Related

Count a foreach loop and create new element in php

I'm working on a Mega Menu for WooCommerce Product Category. I'm able to get the list of all Subcategories using the code below;
$parent_id = 37; //ID of the Parent Category
$subCat_of_parent = get_terms('product_cat',array('child_of' => $parent_id));
Then used in a html structure as below;
<div class="row">
<div class="col-md-6">
<ul>
<?php
foreach ($subCat_of_parent as $subcat) {
?>
<li>
<?php echo $subcat->name; ?>
</li>
<?php
}
?>
</ul>
</div>
</div>
This worked by getting all the list of subcategory of the parent category in this format;
<div class="row">
<div class="col-md-6">
<ul>
<li><a>1st subcategory</a></li>
<li><a>2nd subcategory</a></li>
<li><a>3rd subcategory</a></li>
<li><a>4th subcategory</a></li>
<li><a>5th subcategory</a></li>
<li><a>6th subcategory</a></li>
</ul>
<div>
</div>
what i want to achieve is after the 3rd subcategory, it should break and continue on a new column, so that i can get something like this;
<div class="row">
<div class="col-md-6">
<ul>
<li><a>1st subcategory</a></li>
<li><a>2nd subcategory</a></li>
<li><a>3rd subcategory</a></li>
</ul>
<div>
<div class="col-md-6">
<ul>
<li><a>4th subcategory</a></li>
<li><a>5th subcategory</a></li>
<li><a>6th subcategory</a></li>
</ul>
<div>
</div>
How can i achieve this? Thanks for your help in advance
You can use array_chunk() to divide the array into groups of 3.
<div class="row">
<?php
$chunks = array_chunk($subCat_of_parent, 3);
foreach ($chunks as $group) {
print '<div class="col-md-6">';
print '<ul>';
foreach ($group as $subcat) {
print '<li>';
//to-do
print '</li>';
}
print '</ul>';
print '</div>';
}
?>
</div>
You need to emit the inner <div class="col-md-6"><ul> and the </ul></div> part every three categories.
Here is the pseudocode:
Emit <div class="row">
Set a counter, something like $i = 0
Start your for loop, foreach ($subCat_of_parent as $subcat) {
Now say if ($i == 0), emit the start div tag and start ul tag.
Emit your list item
$i = ($i + 1) % 3
Now say if ($i == 0), emit the close ul tag and close div tag.
If the number of subcategories is not a multiple of three, you need extra logic at the end to make sure the last group is properly closed off.
<?php
$arr = array(1, 2, 3, 4,5,6);
$count = 0;
?>
<div class="row">
<?php
foreach ($subCat_of_parent as $subcat)
{
// echo "count =".$count;
if($count%3 == 0)
{
echo ('<div class="col-md-6">
<ul>');
}
$count++;
?>
<li>
<?php echo $subcat->name; ?>
</li>
<?php
if($count%3 == 0)
{
echo ('
</ul></div>');
}
}
?>
</div>
Below code is working, You can use this.
<?php
$subCat_of_parent = array('1st sub', '2nd sub', '3rd sub', '4th sub', '5 sub', '6 sub', '7 sub');
$subCat_of_parent = array_chunk($subCat_of_parent, 3);
?>
<div class="row">
<?php
foreach ($subCat_of_parent as $subcats) {
?>
<div class="col-md-6">
<ul>
<?php
foreach ($subcats as $subcat) {
?>
<li><?php echo $subcat; ?></li>
<?php
}
?>
</ul>
</div>
<?php
}
?>
</div>

creating class active dynamically in php

I am creating a menu dynamically based on the categories registered on the dataBase, What I need is:
When someone click in the link, this option should have the css class 'active', showing in with page the user is and even the pages are created dynamically.
I have no idea how to do this because I am php student and I couldn't find this information in google for "Dynamically menus" Thank you very much.
<nav class="menu">
<ul class="list">
<li class="line"><a id="item" href="index.php">Home</a></li>
<?php
$categories = $db->select("select * from tbl_category");
if($categories){
while ($result = $categories->fetch_assoc()){
echo <<<HTML
<li id="line" ><a class="item" href="categories.php?category=$result[id]">$result[name]</a></li>
HTML;
}
}
?>
</ul>
<nav>
First of all, you're looping through that while loop and adding the id of "line" to each and every <li> element. I suggest you create unique id's for each list item. I don't necessary advocate using the code the way you have it, just as an example, here's your code edited to do just that:
if($categories){
$i = 1;
while ($result = $categories->fetch_assoc()){
$i++;
echo <<<HTML
<li id="line$i" ><a class="item" href="categories.php?category=$result[id]">$result[name]</a></li>
HTML;
}
}
Then when someone clicks on your link, just use jQuery with something like this:
$(".item").click(function() {
$(this).parent('li').addClass('active');
});
Try something like that:
<nav class="menu">
<ul class="list">
<li class="line"><a id="item" href="index.php">Home</a></li>
<?php
$current_page = isset($_GET['category']) ? $_GET['category']: -1;
$categories = $db->select("select * from tbl_category");
if($categories){
while ($result = $categories->fetch_assoc()){
echo '<li id="line">';
if($result['category'] === $current_page) {
// If we're currently in the active page then add the active class to the <a>
echo '<a class="item active" href="categories.php?category='. $result[id] .'">';
} else {
echo '<a class="item" href="categories.php?category='. $result[id] .'">';
}
echo $result['name'];
echo '</a>';
echo '</li>';
}
}
?>
</ul>
<nav>
Thank you all for help, based on your code, I solved doing this:
menu.php:
<nav class="menu" id="menu">
<ul id="list" class="list">
<li id="line" class="<?php if($presentPage == '0')echo 'active';?>"><a id="item" class="item" href="index.php">Home</a></li>
<?php
function checked($pp, $id){
if($pp == $id){
$status = 'active';
return $status;
}
}
$query = "select * from tbl_category";
$category = $db->select($query);
if($category){
while ($result = $category->fetch_assoc()){
echo "
<li id='line' class='".checked($presentPage, $result['id'])."'><a id='item' class='item' href='categories.php?category=".$result['id']."'>".$result['name']."</a></li>
";//end echo
}//end while
}//end if
?>
</ul>
</nav>
In my index.php:
<?php $presentPage = 0;?>
<?php require_once 'header.php';?>
<?php require_once 'menu.php';?>
and in my categories.php:
<?php
if (!isset($_GET['category']) || $_GET['category'] == NULL) {
$presentPage = 0;
}else{
$presentPage = intval($_GET['category']);//just to make sure that the variable is a number
}
?>
<?php require_once 'header.php';?>
<?php require_once 'menu.php';?>
///...my code...
and in my CSS of course:
.active{
background: linear-gradient(#821e82, #be5abe);
}
Thats is working perfectly for me, Thank for all help.

How to display magento product categories in column wise

I'm trying to display all the categories below the main title, so I want to display four categories in each column. The below is my code
<?php
$_category = $this->getCurrentCategory();
$collection = Mage::getModel('catalog/category')->getCategories($_category->entity_id);
$helper = Mage::helper('catalog/category');
?>
<ul class="pull-left">
<?php foreach ($collection as $cat):?>
<li>
<a style="color:#fff;" href="<?php echo $helper->getCategoryUrl($cat);?>">
<cite><?php echo $cat->getName();?><?php
?></cite>
</a>
</li>
<?php endforeach;?>
</ul>
HTML Output
<ul class="pull-left">
<li>....</li>
<li>....</li>
<li>....</li>
<li>....</li>
<li>....</li>
<li>....</li>
<li>....</li>
<li>....</li>
.
.
.
.
</ul>
I want to define four li items in every ul. Like this
<ul class="pull-left">
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
</ul>
<ul class="pull-left">
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
</ul>
I also want to display product count and I've tried this
<?php
$products_count = Mage::getModel('catalog/category')->load($categoryId)->getProductCount();
?>
but it is not working.
I am using this code on my category page and it is giving me count.
Please have a look at the code I have added before tag.
<?php
$loadCategory = Mage::registry('current_category');
//echo 'loadCategory = '.$_categoryID = $loadCategory->getId();
$subCategories = explode(',', $loadCategory->getChildren());
?>
<ul class="pull-left">
<?php $i=1;
foreach ( $subCategories as $subCategoryId )
{
$cat = Mage::getModel('catalog/category')->load($subCategoryId);
if($cat->getIsActive())
{
if($cat->getImageUrl())
{
//echo ''.$cat->getName().'</br>';
echo '<li>';
echo '<a style="color:#fff;" href="'.$cat->getUrl().'">'.$this->escapeHtml($cat->getName()); echo"<br>";
echo $total = Mage::getModel('catalog/layer')->setCurrentCategory($subCategoryId)->getProductCollection()->getSize();
echo'</a>';
echo '</li>';
if($i%4==0)
{
echo'</ul><ul class="pull-left">';
}
}
}
$i++;
}
?>
Please use this code. This will surely work for you.
<?php
$category = Mage::getModel('catalog/category')->load($categoryID);
echo $total = Mage::getModel('catalog/layer')->setCurrentCategory($category)->getProductCollection()->getSize();
?>
Thanks,
Please use this one.
You can adjust ul ,li as per wish. Right now this will list four subcategories in a single row.
This code will give you the subcategories of a parent category.
<?php
$loadCategory = Mage::registry('current_category');
//echo 'loadCategory = '.$_categoryID = $loadCategory->getId();
$subCategories = explode(',', $loadCategory->getChildren());
?>
<ul class="pull-left">
<?php $i=1;
foreach ( $subCategories as $subCategoryId )
{
$cat = Mage::getModel('catalog/category')->load($subCategoryId);
if($cat->getIsActive())
{
if($cat->getImageUrl())
{
//echo ''.$cat->getName().'</br>';
echo '<li>';
echo '<a style="color:#fff;" href="'.$cat->getUrl().'">'.$this->escapeHtml($cat->getName()).'</a>';
echo '</li>';
if($i%4==0)
{
echo'</ul><ul class="pull-left">';
}
}
}
$i++;
}
?>
</ul>

Include PHP within a variable?

Say I have
$output = '';
and I want to include the following code within the double ''.
<ul class="nav megamenu">
<?php if (!$logged) { ?>
<li class="home">
<a href="?route=common/home">
<span class="menu-title">Home</span>
</a>
</li>
<?php } ?>
<?php if ($logged) { ?>
<li class="home">
<a href="?route=subscribers/home">
<span class="menu-title">Home</span>
</a>
</li>
<?php } ?>
How could I go about doing this?
You could use output buffering, something like this:
<?php ob_start();?>
<ul class="nav megamenu">
<?php if (!$logged) { ?>
<li class="home">
<a href="?route=common/home">
<span class="menu-title">Home</span>
</a>
</li>
<?php } ?>
<?php if ($logged) { ?>
<li class="home">
<a href="?route=subscribers/home">
<span class="menu-title">Home</span>
</a>
</li>
<?php } ?>
<?php
$output = ob_get_contents();
ob_end_clean();
?>
See In Action
Using this method of starting a buffer and then assigning the result to a variable is very handy and is used in some MVC frameworks.
Simple example:
<?php
/* Assign an array of values that will be passed
* to the loader then extracted into local variables */
$data['logged']=true;
$output = loadContentView('top_nav', $data);
function loadContentView($view, $data=null){
$path = SITE_ROOT.'/path/to/views/'.$view.'.php';
if (file_exists($path) === false){
return('<span style="color:red">Content view not found: '.$path.'</span>');
}
/* Extract $data passed to this function */
if($data != null){
extract($data);
}
ob_start();
require($path);
$return = ob_get_contents();
ob_end_clean();
return $return;
}
?>
I just changed my code to this instead... much easier
Thanks everyone for all the help!
<?php
if (!$this->customer->isLogged()) {
$output = '<ul class="nav megamenu"><li class="home"><span class="menu-title">Home</span></li>';
}else{
$output = '<ul class="nav megamenu"><li class="home"><span class="menu-title">Home</span></li>';
}
?>

Magento - list sub-categories of a specific parent category as links

I am a beginner to php and am stuck on trying to call out sub-categories of just one parent category as links
I got to this and it’s bringing up the getName but the getUrl() isn’t returning any URL at all....
<?php
$children = Mage::getModel('catalog/category')->getCategories(3);
foreach ($children as $category):
echo '<li>' . $category->getName() . '</li>';
endforeach;
?>
The output code is just <li>name of sub-cat</li>
Anybody have any ideas? Please?
Thanks,
Kayla
Please try this code I think you did this code but this is very helpful for someone who is searching this code
<?php
$children = Mage::getModel('catalog/category')->getCategories(3);
foreach ($children as $category):
$category = Mage::getModel('catalog/category')->load($category->getId());
echo '<li>' . $category->getName() . '</li>';
endforeach;
?>
I don't know why #Dhanapal solution has not worked for me, so I used:
$categories = Mage::getModel('catalog/category')->load('3')->getChildrenCategories();
foreach ($children as $category):
$category = Mage::getModel('catalog/category')->load($category->getId());
echo '<li>' . $category->getName() . '</li>';
endforeach;
For some reason, the above answer did not work for me. I am also using Magento 1.7.x.
I found Magento displays subcategories description on category list.phtml link to be helpful.
I am trying to do the same as you so I adjusted the above answer from
<?php $children = explode( ",", $this->getCurrentCategory()->getChildren() ); ?>
<div class="category-products">
<ul class="products-grid">
<?php foreach( $children as $child ): ?>
<?php $_child = Mage::getModel( 'catalog/category' )->load( $child ); ?>
<li class="item"><?php echo $_child->getDescription(); ?></li>
<?php endforeach; ?>
</ul>
</div>
to:
<?php $children = explode( ",", $this->getCurrentCategory()->getChildren() ); ?>
<div class="category-products">
<ul class="products-list">
<?php foreach( $children as $child ): ?>
<?php $_child = Mage::getModel( 'catalog/category' )->load( $child ); ?>
<li class="item"> <?php echo $_child->getName() ?> </li>
<?php endforeach; ?>
</ul>
</div>
Just like the above answer, you can change the div class to
<div class="products-grid">
instead of listing them. But you have asked how to list.
I hope this helps people in the future. There are tons of other related questions on Stack overflow as well.
Well, This was a pain in the ass. I've made a function for it that lists all the subcategories and sub-sub-categories of the category that you want to display.
<?PHP
//get the children of the current category
function getChildrenInterStore($id) {
$returnstring = '';
$subCats = Mage::getModel('catalog/category')->load($id)->getChildren();
//get sub category ids
$subCatIds = explode(',',$subCats);
if (count($subCatIds) > 0):
foreach($subCatIds as $subCatId):
$subCat = Mage::getModel('catalog/category')->load($subCatId);
if($subCat->getIsActive()):
$returnstring .= '
<li class="other-toggle sm_megamenu_lv1 sm_megamenu_drop parent">
<a class="sm_megamenu_head sm_megamenu_drop" href="'.$subCat->getUrl().'">
<span class="sm_megamenu_icon">
<span class="sm_megamenu_title">'.$subCat->getName().'</span>
</span>
</a>
</li>';
$returnstring .= getChildrenInterStore($subCatId);
endif;
endforeach;
endif;
return $returnstring;
}
?>
<div class="mega-left-title">
<strong>Categorieen</strong>
</div>
<div class="css_effect sm_megamenu_wrapper_vertical_menu sambar">
<div class="sambar-inner">
<ul class="sm-megamenu-hover sm_megamenu_menu sm_megamenu_menu_black">
<?PHP echo getChildrenInterStore('17'); ?>
</ul>
</div>
</div>

Categories