I want to create a recursive menu with the product totals included, however I am stuck now because I already rendered the Topmenu however i cant find another way to do it.
I dont want to use alot of MySQL Queries because it can make my site very slow.
The code i am using:
require 'db.php';
$result = mysql_query("SELECT COUNT(c.category_id) AS count, c. category_id, c.parent_id, cd.name, p.product_id FROM category c
LEFT JOIN category_description AS cd ON (cd.category_id=c.category_id)
LEFT JOIN product_to_category AS ptc ON (ptc.category_id=c.category_id)
LEFT JOIN product AS P ON (p.product_id=ptc.product_id)
GROUP BY c.category_id
ORDER BY c.parent_id,cd.name") or die (mysql_error());
$menuData = array( 'items' => array(), 'parents' => array() );
while ($menuItem = mysql_fetch_assoc($result)) {
$menuData['items'][$menuItem['category_id']] = $menuItem;
$menuData['parents'][$menuItem['parent_id']][] = $menuItem['category_id'];
}
function buildMenu($parentId, $menuData) {
$html = '';
if (isset($menuData['parents'][$parentId]))
{
$html = '<ul>';
foreach ($menuData['parents'][$parentId] as $itemId) {
$iCount = ($menuData['items'][$itemId]['product_id'] != NULL) ? $menuData['items'][$itemId]['count'] : '0';
$html .= '<li>' . $menuData['items'][$itemId]['name'] . ' (' . $iCount . ') ';
$html .= buildMenu($itemId, $menuData);
$html .= '</li>';
}
$html .= '</ul>';
}
return $html;
}
echo buildMenu(0, $menuData);
The expected output:
Dell (1)
--Computer(1)
---DataCable(1)
----Extra Sub (0)
Current output:
Dell (0)
--Computer(0)
---DataCable(1)
----Extra Sub (0)
I think your query returns wrong result. Use
print '<pre>';print_r($menuData);print '</pre>'; before calling buildMenu() function and see whether query returns right data or not.
I think this should get you closer to the right direction:
foreach ($menuData['parents'][$parentId] as $itemId) {
$menu = buildMenu($itemId, $menuData);
$item = $menuData['items'][$itemId];
$iCount = ($item['product_id'] != NULL) ? $item['count'] : '0';
$menuData['items'][$parentId]['count'] += $iCount;
$html .= '<li>' . $item['name'] . ' (' . $iCount . ') ';
$html .= $menu
$html .= '</li>';
}
By re-ordering and then adding the current item's count to the parent item's count, you can make sure that when you output iCount, it will include all of the children's counts as well.
I also used a temporary variable for $menuData['items'][$itemId]. Because you're only doing three array lookups on that $menuData, it might not be substantially more efficient, but it is much easier to read.
Related
I'm trying to build this function that display a menu with sub menus from a database. Something that can show like sub-sub-sub-menus and so on... If that's ever something I need.
In the database i have 2 tables, menu and menuItem.
menu
menuItem
php file
function get_menu($id) {
require(dirname(__FILE__) . '/config.php');
// main base
$menu = " SELECT id, menu_name, menu_data, menu_order
FROM mos_menuItem
WHERE menu_parent_id='$id'
AND menu_sub='0'
ORDER BY menu_order";
$menuResult = mysqli_query($conn, $menu);
echo '<ul>';
while ($menuRow = mysqli_fetch_row($menuResult)) {
// main menu
echo '<li>'. $menuRow[1] .'</li>';
// sub base
$sub = "SELECT id, menu_name, menu_data, menu_order, menu_sub
FROM mos_menuItem
WHERE menu_parent_id='$menuRow[0]'
AND menu_sub>='1'
ORDER BY menu_order";
$subResult = mysqli_query($conn, $sub);
echo '<ul>';
while ($subRow = mysqli_fetch_row($subResult)) {
echo '<li>'. $subRow[1] .'</li>';
}
echo '</ul>';
}
echo '</ul>';
mysqli_close($conn);
}
so I need to loop something inside itself? I'm not sure where to go from here.
Recursion is your best friend here.
I am not sure what purpose is served by the menu_sub field. I assume it tells you at what level it is in the menu hierarchy. If that is the case, then you can safely ignore it (menu_parent_id would suffice) to create this multi-level menu hierarchy.
function getMenus($menu_id, $is_sub=false){
$sql = "SELECT id, menu_name, menu_data, menu_order
FROM mos_menuItem
WHERE menu_parent_id='$menu_id'
ORDER BY menu_order";
$mResult = mysqli_query($conn, $sub);
echo '<ul>';
while ($mRow = mysqli_fetch_row($mResult)) {
echo '<li>'. $mRow[1] .'';
getMenus($mRow[0], true);
echo '</li>';
}
echo '</ul>';
}
This does have one drawback where if you want the hierarchy to start from a parent other than 0 (I assume your root menu_id is 0), that particular menu would not show up in the hierarchy.
So after some trial and error i figure it out. It really helped to look at Sheikh Azad code.
The only difference is in my function i have AND menu_sub='$sub' to make it not going loop the main menu item if the id is the same.. If that make sense.
The function
function mos_get_menu_editor($menuId, $sub) {
require(dirname(__FILE__) . '/config.php');
$item = " SELECT id, menu_name, menu_data, menu_order, menu_sub
FROM mos_menuItem
WHERE menu_parent_id='$menuId'
AND menu_sub='$sub'
ORDER BY menu_order";
$itemResult = mysqli_query($conn, $item);
echo '<ul>';
while ($itemRow = mysqli_fetch_row($itemResult)) {
echo '<li>'. $itemRow[1] .'</li>';
mos_get_menu_editor($itemRow[0], 1);
}
echo '</ul>';
mysqli_close($conn);
}
I need to display my category tree as a list in a responsive menu.
The idea is to display the highest level categories. and create dynamically a list that will be displayed for each category that has children.
I stumbled upon a code that helped me a bit, but i can't figure how to get the job done.
Here is the code:
<?php
$rootCatId = Mage::app()->getStore()->getRootCategoryId();
function getTreeCategories($parentId, $isChild){
$allCats = Mage::getModel('catalog/category')->getCollection()
->addAttributeToSelect('*')
->addAttributeToFilter('is_active','1')
->addAttributeToFilter('include_in_menu','1')
->addAttributeToFilter('parent_id',array('eq' => $parentId));
$class = ($isChild) ? "sub-cat-list" : "cat-list";
$html .= '<ul class="'.$class.'">';
$children = Mage::getModel('catalog/category')->getCategories(7);
foreach ($children as $category) {
{
$html .= '<li>'.$category->getName()."";
$subcats = $category->getChildren();
if($subcats != ''){
$html .= getTreeCategories($category->getId(), true);
}
$html .= '</li>';
}
$html .= '</ul>';
return $html;
}
$catlistHtml = getTreeCategories($rootCatId, false);
echo $catlistHtml;
?>
Thank you in advance.
you can use this to create category tree:
<?php
$rootCatId= Mage::app()->getStore()->getRootCategoryId();
$categories = Mage::getModel('catalog/category')->getCategories($rootCatId);
$output= '<ul>';
foreach($categories as $category) {
$cat = Mage::getModel('catalog/category')->load($category->getId());
$count = $cat->getProductCount();
$output .= '<li>' . '' . $category->getName() . "";
if ($category->hasChildren()) {
$children = Mage::getModel('catalog/category')->getCategories($category->getId());
$array .= get_categories($children);
}
$output .= '</li>';
}
echo $output . '</ul>';
?>
For the effects of showing / hiding categories and subcategories, you can use plain css or jQuery / Prototype.
This script displays the categories of a post, but excludes the ones that the user doesn't want to show:
function exclude_post_categories($excl='', $spacer=' ') {
$categories = get_the_category($post->ID);
if (!empty($categories)) {
$exclude = $excl;
$exclude = explode(",", $exclude);
$thecount = count(get_the_category()) - count($exclude);
foreach ($categories as $cat) {
$html = '';
if (!in_array($cat->cat_ID, $exclude)) {
$html .= '<a href="' . get_category_link($cat->cat_ID) . '" ';
$html .= 'title="' . $cat->cat_name . '">' . $cat->cat_name . '</a>';
if ($thecount > 1) {
$html .= $spacer;
}
$thecount--;
echo $html;
}
}
}
}
The fuctions is triggered like this.
<?php exclude_post_categories('5', ', ');
So if a post has the categories: 1,2,3,4,5 than only 1,2,3,4 are echoed.
The script works great for the posts that have the category that is excluded (5).
The problem lies with the posts that don't have that category.
So if a post has the categories: 1,2,3,4 that those are echoed but with less commas than needed: 1,2,34
$thecount variable is always calculated wrong for the posts that don't have the category that has to be excluded.
Try something like this:
$existing = get_the_category();
$newcategories = array_udiff($existing,$exclude,function($e,$x) {
return $e->cat_ID != $x;
});
$as_links = array_map(function($c) {
return '<a href="'.get_category_link($c->cat_ID).'" '
.'title="'.$cat->cat_name.'">'.$cat->cat_name.'</a>';
},$newcategories);
echo implode($spacer, $as_links);
This will first strip out categories whose IDs are in the $exclude array, then convert each category to a category link, before outputting them with the separator.
EDIT: Slightly mis-read the question. This expects $exclude to be an array. Put the following line at the start:
if( !is_array($exclude)) $exclude = array($exclude);
To make it support single-value inputs as well - this way you can either specify one or many categories to exclude.
Found a better solution to the problem here: http://css-tricks.com/snippets/wordpress/the_category-excludes/#comment-1583708
function exclude_post_categories($exclude="",$spacer=" ",$id=false){
//allow for specifiying id in case we
//want to use the function to bring in
//another pages categories
if(!$id){
$id = get_the_ID();
}
//get the categories
$categories = get_the_category($id);
//split the exclude string into an array
$exclude = explode(",",$exclude);
//define array for storing results
$result = array();
//loop the cats
foreach($categories as $cat){
if(!in_array($cat->cat_ID,$exclude)){
$result[] = "$cat->name";
}
}
//add the spacer
$result = implode($spacer,$result);
//print out the result
echo $result;
}
So I'm trying to create a dynamic navigation bar that is built through menu items stored in a database.
Each menu item is assigned a parent_id, if that id is 0, that item is a parent; if that id is a value above 0, then that menu item is the child of another menu item. (Imagine hovering over a navigation menu item and getting a drop down of children items)
The following function is what builds the multi-dimensional array from the records in the database.
function build_menu($menu_id){
$mysqli = new mysqli("localhost", "user", "password", "db");
if($mysqli->connect_errno){
echo "Failed to connect to MySQL: " . $mysqli->connect_errno;
}
$stmt = $mysqli->prepare("SELECT `id`, `name`, `order`, `parent_id`, `path` FROM menu_items WHERE `group_id` = ? ORDER BY `order` ASC");
$menu_id = intval($mysqli->real_escape_string($menu_id));
$stmt->bind_param('i', $menu_id);
if($stmt->execute()){
$results = resultToArray($stmt->get_result());
$parents = array();
$children = array();
$menu = array();
for($i=0; $i<count($results); $i++){
if($results[$i]['parent_id'] === 0){
$parents[] = $results[$i];
} else {
$children[] = $results[$i];
}
}
for($i=0; $i<count($parents); $i++){
$menu[$i] = $parents[$i];
for($j=0; $j<count($children); $j++){
if($children[$j]['parent_id'] === $parents[$i]['id']){
$menu[$i][$j] = $children[$j];
}
}
}
return($menu);
} else {
echo "Failed to execute statement: " . $stmt->errno . ": " . $stmt->error;
}
$mysqli->close();
return(0);
}
This is the function that takes that array and turns it into HTML code to be used as the navigation bar.
function get_menu($menu_id) {
if(build_menu($menu_id)){
$menu = build_menu($menu_id);
$menu_html = '<ul>';
for($i=0; $i<count($menu); $i++){
$menu_item_path = ($menu[$i]['path'] != NULL ? $menu[$i]['path'] : '#');
$menu_html .= '<li>' . $menu[$i]['name'] . '';
if(count($menu[$i])>0){
$menu_html .= '<ul>';
for($j=0; $j<count($menu[$i]); $j++){
if(isset($menu[$i][$j])){
$menu_html .= '<li>' . $menu[$i][$j]['name'] . '</li>';
}
}
$menu_html .= '</ul>';
}
$menu_html .= '</li>';
}
$menu_html .= '</ul>';
return($menu_html);
}
}
I presumed that if(count($menu[$i])>0) would count the array inside the selected element, but for some reason, it counts the keys and therefore always true.
I know this is a pretty long question, I do appreciate those of you that have read this far. If you can help me figure out a way to see if a menu item has children items, that would be great.
use array_filter to count only the items in array not empty or null
Process of generating HTML menu can be a lot easier than your example.
function generateMenu(array $menu, $parentId = 0) {
if (!isset($menu[$parentId])) {
return;
}
$output = '<ul>';
foreach ($menu[$parentId] as $record) {
$output .= '<li>';
$output .= '' . $record['title'] . '';
$output .= generateMenu($menu, $record['id']);
$output .= '</li>';
}
$output .= '</ul>';
return $output;
}
echo generateMenu($menu);
Above code would output HTML like:
x First level #1
o Second level #8
x First level #2
o Second level #4
- Third level #6
- Third level #7
o Second level #5
x First level #3
demo
But for that to work, you must fix your build_menu() function, to return array like this, which is very easy, just create one array $menu and fill it like $menu[$row['parent_id']][] = $row;, where $row is record row fetched from database.
Question: How can I tie together my query with my function? I would like to print my list, but I am not sure how to connect the two. Also, am I using "entries" correctly below?
/* data handling */
$result = mysql_query("SELECT * FROM Items LEFT JOIN Categories on Categories.CategoryID = Items.FK_CategoryID WHERE Items.FK_UserID = $_SESSION[user_id] ORDER BY CategorySort, CategoryName ASC, ItemSort, ItemTitle");
/* output logic */
function render_list($ItemTitle, array $entries)
{
echo '<ul><li>' . $ItemTitle . '<ul>';
foreach($entries as $entry)
{
echo '<li>' . $entry['ItemID'] . '</li>';
}
echo '</ul></li></ul>';
}
render_list();
Do I need to use a while clause?
// loop through topics
while($row = mysql_fetch_array($result)) {
render_list;
}
Yeah, replace your foreach loop with that while loop. Take that "type" (array) out of the function definition though, you don't need to do that in loosely typed php language. Here's a rework of the code you have:
/* output logic */
function render_list($ItemTitle, $entries)
{
echo '<ul><li>' . $ItemTitle . '<ul>';
while($entry = mysql_fetch_array($entries))
{
echo '<li>' . $entry['ItemID'] . '</li>';
}
echo '</ul></li></ul>';
}
$result = mysql_query("SELECT * FROM Items LEFT JOIN Categories on Categories.CategoryID = Items.FK_CategoryID WHERE Items.FK_UserID = $_SESSION[user_id] ORDER BY CategorySort, CategoryName ASC, ItemSort, ItemTitle");
render_list('My Title', $result);