PDA

View Full Version : PHP Category Tree


FrEaKmAn
March 1st, 2007, 08:38 PM
Hi

Can somebody help me set up nice category tree listing, so from making right database structure and then exporting this in php...

Buzz
March 1st, 2007, 11:55 PM
Need a bit more description on what you need in terms of functionality and interface.

FrEaKmAn
March 2nd, 2007, 12:29 AM
http://shrani.si/files/categoryupsn.gif

basically we could replace categories with pages, so that we have subpages then..

Buzz
March 2nd, 2007, 01:08 AM
It seems pretty straigh-foraward.

MySQL schema:

page
id | level | category | path | title

You can then loop through the levels and categories based on db info. When adding a page, reference the level and category that it falls under.


$nav = mysql_query("SELECT * FROM page ORDER BY id DESC") or die('No data' . mysql_error());

echo '<ul>';
while ($pages = mysql_fetch_array($nav)) {
$title=$pages->title;
$path=$pages->path;
$level=$pages->level;
$cat=$pages->category;
$id=$pages->id;

if ($cat == 1) {
if ($level == 1) { echo '<li class="lvl1"><a hef="'.$path.'">'.$title.'</a></li>' }
if ($level == 2) { echo '<li class="lvl2"><a hef="'.$path.'">'.$title.'</a></li>' }
if ($level == 3) { echo '<li class="lvl3"><a hef="'.$path.'">'.$title.'</a></li>' }
}

if ($cat == 2) {
if ($level == 1) { echo '<li class="lvl1"><a hef="'.$path.'">'.$title.'</a></li>' }
if ($level == 2) { echo '<li class="lvl2"><a hef="'.$path.'">'.$title.'</a></li>' }
if ($level == 3) { echo '<li class="lvl3"><a hef="'.$path.'">'.$title.'</a></li>' }
}
}
echo '</ul'>;


This is untested but it's fairly simple.

could probably use a foreach loop inside the while loop for the categories as well. There's some good foreach code here: http://us3.php.net/foreach

degsy
March 2nd, 2007, 10:05 AM
I would setup a table for the categories and a table to associate a catid with a subcat

This way you can do a recursive loop to output your menu.

Categories
CatId, Title
1 Category1
2 Category2
3 Category3
4 SubCategory1
5 SubCategory2
6 SubSubCategory1

SubCat
CatId ParentId
1 0
2 0
3 0
4 2
5 2
6 5

What this means when you create your query is to first output the toplevel categories you would select all CatID where ParentId is Zero

CatID6 (SubSubCategory) is a subcat of ParentId 5 (SubCategory2)


Then within your loop you would output any Categoeries for the current CatID. As this is a recursive loop it will output all of your Categories, SubCategories, SubSubCategories etc.


If I have time later I will knock up an example

FrEaKmAn
March 2nd, 2007, 01:31 PM
Hi

Thanx for help. Well I was able to set similar thing as you mentioned the only problem was that I didn't know how to make loop for sub, subsub, subsubsub...

Buzz
March 2nd, 2007, 01:54 PM
I thought of that method as well degs.. good call :)

I then decided to simplify since the were all navigation and when you get into subcats of subcats it can get very convoluted. Adding a level to the link/category keeps it easier to deal with. At least it would for me.

degsy
March 2nd, 2007, 03:14 PM
Here is an example
http://www.degs.co.uk/test/menu_system/menu_system.php


-- phpMyAdmin SQL Dump
-- version 2.6.4-pl4
-- http://www.phpmyadmin.net
-- --------------------------------------------------------
--
-- Table structure for table `menu_system_categories`
--
CREATE TABLE `menu_system_categories` (
`cat_id` int(5) NOT NULL auto_increment,
`cat_title` varchar(255) NOT NULL default '',
PRIMARY KEY (`cat_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;
--
-- Dumping data for table `menu_system_categories`
--
INSERT INTO `menu_system_categories` VALUES (1, 'Category1');
INSERT INTO `menu_system_categories` VALUES (2, 'Category2');
INSERT INTO `menu_system_categories` VALUES (3, 'Category3');
INSERT INTO `menu_system_categories` VALUES (4, 'Category4');
INSERT INTO `menu_system_categories` VALUES (5, 'SubCategory1');
INSERT INTO `menu_system_categories` VALUES (6, 'SubCategory2');
INSERT INTO `menu_system_categories` VALUES (7, 'SubSubCategory1');
-- --------------------------------------------------------
--
-- Table structure for table `menu_system_sub_categories`
--
CREATE TABLE `menu_system_sub_categories` (
`sub_cat_id` int(5) NOT NULL default '0',
`cat_id` int(5) NOT NULL default '0',
PRIMARY KEY (`sub_cat_id`,`cat_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Dumping data for table `menu_system_sub_categories`
--
INSERT INTO `menu_system_sub_categories` VALUES (1, 0);
INSERT INTO `menu_system_sub_categories` VALUES (2, 0);
INSERT INTO `menu_system_sub_categories` VALUES (3, 0);
INSERT INTO `menu_system_sub_categories` VALUES (4, 0);
INSERT INTO `menu_system_sub_categories` VALUES (5, 2);
INSERT INTO `menu_system_sub_categories` VALUES (6, 2);
INSERT INTO `menu_system_sub_categories` VALUES (7, 6);



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Menu System</title>
</head>
<body>
<?php
function list_categories($catid,$counter){
// set database server access variables:
//$host = "localhost";
//$user = "";
//$pass = "";
//$db = "";
// open connection
//$connection = mysql_connect($host, $user, $pass) or die ("Unable to connect!");
// select database
//mysql_select_db($db) or die ("Unable to select database!");
require('../php/db.php');
// create query
$query = "SELECT c.cat_id, c.cat_title FROM menu_system_categories c, menu_system_sub_categories s WHERE c.cat_id = s.sub_cat_id AND s.cat_id = " . $catid;
// execute query
$result = mysql_query($query) or die ("Error in query: $query. ".mysql_error());
// see if any rows were returned
if (mysql_num_rows($result) > 0) {
// yes
// print them one after another
$space = str_repeat('&nbsp;',$counter*10);
while($row = mysql_fetch_row($result)) {
$cat_title = $row[1];
$this_catid = $row[0];

// just some quick formatting. Use lists or css for better results.
echo $space . $cat_title;
echo '<br>';
// increase the counter for the spacing in the next loop
$counter++;
// call the recursive function to list any subcategories
list_categories($this_catid,$counter);
// decrease the counter to correct the spacing
$counter--;
}
}
// free result set memory
mysql_free_result($result);
// close connection
//mysql_close($connection);
}
$top_level_catid = 0;
$counter_start = 0;
list_categories($top_level_catid,$counter_start);
?>
</body>
</html>



I've just outputted using linebreaks and spacing.
You can get better results if you use lists and/or CSS

Here is an example using the same method but formatted to output in a list
It's in ASP, but the logic and method is the same
http://computer-helpforum.com/asp/aspfree/MenuSystem/DHTMLTree/MenuSystem_DHTMLTree_Tutorial.asp



and incorporating a javascript menu
http://www.mattkruse.com/javascript/mktree/index.html

http://computer-helpforum.com/asp/aspfree/MenuSystem/DHTMLTree/MenuSystem_DHTMLTree.asp

FrEaKmAn
March 2nd, 2007, 04:29 PM
wow, thanks alot. I'll post my script when I insert this and add option for inputing category...

Buzz
March 2nd, 2007, 08:55 PM
Nice stuff Degs :) Thanks

FrEaKmAn
March 25th, 2007, 03:36 PM
ok so I have one more question about this. How can I access these categories. I know I could simply access them by locating every category but I also want to have all categories showed in link in which the current cat is.I don't how this goes but I want to use clean links in future so I must first output all categories in link. For example if I have

Cat 1
- Subcat 1
- SubSubcat 1

that my link looks like index.php?cat=1&subcat=1&subsubcat=1

or how ever this goes...

degsy
March 29th, 2007, 02:55 PM
Here is an example of a breadcrumb script

function breadcrumb($catid){
require('../php/db.php');
// create query
$query = "SELECT menu_system_categories.cat_title, menu_system_categories.cat_id as cat_id, menu_system_sub_categories.cat_id as sub_cat_id
FROM menu_system_categories, menu_system_sub_categories
WHERE menu_system_categories.cat_id = menu_system_sub_categories.sub_cat_id
AND menu_system_categories.cat_id = " . $catid;
// execute query
//$result = mysql_query($query) or die ("Error in query: $query. ".mysql_error());
$rs = mysql_query($query) or die(mysql_error());
$row_rs = mysql_fetch_assoc($rs);
$totalRows_rs = mysql_num_rows($rs);
// see if any rows were returned

if ($totalRows_rs > 0) {
$new = array($row_rs['cat_title'] => $row_rs['cat_id']);
$arr = array_merge(breadcrumb($row_rs['sub_cat_id']),$new);
}

return $arr;
}

$cid=0;
$count = 0;
$counter_start = 0;
if(isset($_GET['cid'])){
$cid = $_GET['cid'];
}
if(!is_numeric($cid)){
echo 'Invalid Category';
}
else{
echo '<p><a href="' . $_SERVER['SCRIPT_NAME'] . '">Home</a>';
$catlist = breadcrumb($cid);
$catlist_count = count($catlist);
if($catlist_count > 0){
foreach($catlist as $title => $id){
echo ' &gt; ';
if($count == $catlist_count-1){
echo $title;
}
else{
echo '<a href="?cid=' . $id . '">' . $title . '</a>';
}
$count++;
}
}

echo '</p>';

list_categories($cid,$counter_start);
}
?>

http://degs.co.uk/test/menu_system/breadcrumb.php?cid=7

FrEaKmAn
March 29th, 2007, 09:54 PM
Hi, thnx again for helping me out, that breadcrumb script will be useful. So here is my part of script, I aplogize if it's messy and I know I should change few things but it's works...


<?php
//change the details!!!
$host = "localhost";
$user = "root";
$pass = "";
$db = "10";
// open connection
$connection = mysql_connect($host, $user, $pass) or die ("Unable to connect!");
// select database
mysql_select_db($db) or die ("Unable to select database!");


function output($link){
$parts = explode('/', $link); //first we brake link into parts
//print_r($parts);
//echo count($parts).'<br>';
$parts_count = count($parts); //here we count how many parts we have
if($parts_count == 1){ //if there is only one part so we want to access first cat, we get normal output
$title = $parts[0];
$result = mysql_query("SELECT * FROM menu_system_categories WHERE cat_title = '$title'");
$data = mysql_fetch_array($result);
return $data['cat_id']; //we return the category id for later outputting
}
else {
$check = 1; //this is set to TRUE and checks if second cat is subcategory of the first one
$counter = 0; //counter counts how many outputs so that it only returns the last cat id
for ($i=0;$i<$parts_count;$i++) {
if($check == 0){
return 0;
break;
}
else {
$title1 = $parts[$i]; //first category is title1 which we output with code below
$title2 = $parts[$i+1]; //second category is title2 and we also output this...
//echo $parts[$i];

$result = mysql_query("SELECT * FROM menu_system_categories WHERE cat_title = '$title1'");
$data = mysql_fetch_array($result);

$result2 = mysql_query("SELECT * FROM menu_system_categories WHERE cat_title = '$title2'");
$data2 = mysql_fetch_array($result2);
$cat_id = $data['cat_id']; //cat id for first cat
$cat2_id = $data2['cat_id']; //cat id for second cat

$result3 = mysql_query("SELECT * FROM menu_system_sub_categories WHERE sub_cat_id = '$cat2_id' AND cat_id = '$cat_id'");
$data3 = mysql_fetch_array($result3);
if (mysql_num_rows($result3) > 0){ //with $result3 we check if second cat is subcat of the first one, if yes then we output further otherwise error
if($counter == $parts_count-2){ //here we use counter and we output only last cat id
return $data3['sub_cat_id'];
}
}
else {
$check = 0;
}
}
$counter = $counter+1;
}
}
}

/*these are setting for my server. The script is located in http://localhost/test/10/ and I output $REQUEST_URI where
I remove /test/10/ so that I only get categories. And there is also small problem if I have / on the end of the link, so
I use the code and remove it*/


$link = substr($REQUEST_URI, 9);
if(substr($link, -1) == '/'){
$link = substr($link, 0,-1);
}

//Here you can test different cats manually
//$link = 'Category1';
//$link = 'Category2/SubCategory2';
//$link = 'Category2/SubCategory2/SubSubCategory1';
//echo $link;
$category_id = output($link);
//here we have the last category which we can use for outputting data
echo $category_id;

?>
+ you must have htaccess with this in it:


Options +FollowSymLinks
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* /test/10/index.php [L]
where /test/10/index.php is the location of your script. I have it on my localhost/test/10/index.php

FrEaKmAn
March 31st, 2007, 02:22 AM
ok here is more of the script. Now I added links to category tree:

index.php

<?php
//change the details!!!
$host = "localhost";
$user = "root";
$pass = "";
$db = "10";
// open connection
$connection = mysql_connect($host, $user, $pass) or die ("Unable to connect!");
// select database
mysql_select_db($db) or die ("Unable to select database!");
include 'functions.php';
//edit this to fit your settings. My file is located in http://localhost/test/10/
$link = substr($REQUEST_URI, 9);
if(substr($link, -1) == '/'){
$link = substr($link, 0,-1);
}
$top_level_catid = 0;
$counter_start = 0;
list_categories($top_level_catid,$counter_start);
echo '<br>';
echo 'And here we output the category id: '.output($link);
?>


functions.php

<?php
function create_link($category_id){
$check = 1;
$arr_number = 1;
$a[0] = $category_id;
//echo $category_title;
for($i=0; $i<1000; $i++){
if($check == 0){
//return 'error';
break;
}
else {
$result = mysql_query("SELECT * FROM menu_system_sub_categories WHERE sub_cat_id = '$category_id'");
$data = mysql_fetch_array($result);
if($data['cat_id'] !== '0'){
$category_id = $data['cat_id'];
$a[$arr_number] = $data[cat_id];
$arr_number = $arr_number+1;
}
else {
$check = 0;
}
}
}
sort($a);
$count = count($a);
//print_r($a);
for($j=0; $j<$count; $j++){
$output_id = $a[$j];
$result2 = mysql_query("SELECT * FROM menu_system_categories WHERE cat_id = '$output_id'");
$data2 = mysql_fetch_array($result2);
$b[$j] = $data2['cat_title'];
}
$complete = implode("/", $b);
return $complete;

}

function list_categories($catid,$counter){

$query = "SELECT c.cat_id, c.cat_title FROM menu_system_categories c, menu_system_sub_categories s WHERE c.cat_id = s.sub_cat_id AND s.cat_id = " . $catid;
$result = mysql_query($query) or die ("Error in query: $query. ".mysql_error());

if (mysql_num_rows($result) > 0) {
$space = str_repeat('&nbsp;',$counter*10);
while($row = mysql_fetch_row($result)) {
$cat_title = $row[1];
$this_catid = $row[0];

echo $space .'<a href="http://localhost/test/10/'.create_link($this_catid).'">'.$cat_title.'</a>';
echo '<br>';

$counter++;
list_categories($this_catid,$counter);
$counter--;
}
}
}

function output($link){
$parts = explode('/', $link);
//print_r($parts);
//echo count($parts).'<br>';
$parts_count = count($parts);
if($parts_count == 1){
$title = $parts[0];
if($title == ''){
return 'Front page';
} else {
$result = mysql_query("SELECT * FROM menu_system_categories WHERE cat_title = '$title'");
$data = mysql_fetch_array($result);
if (mysql_num_rows($result) > 0){
return $data['cat_id'];
}
else {
return 0;
}
}
}
else {
$check = 1;
$counter = 0;
for ($i=0;$i<$parts_count;$i++) {
if($check == 0){
return 0;
break;
}
else {
$title1 = $parts[$i];
$title2 = $parts[$i+1];
//echo $parts[$i];

$result = mysql_query("SELECT * FROM menu_system_categories WHERE cat_title = '$title1'");
$data = mysql_fetch_array($result);

$result2 = mysql_query("SELECT * FROM menu_system_categories WHERE cat_title = '$title2'");
$data2 = mysql_fetch_array($result2);
$cat_id = $data['cat_id'];
$cat2_id = $data2['cat_id'];

$result3 = mysql_query("SELECT * FROM menu_system_sub_categories WHERE sub_cat_id = '$cat2_id' AND cat_id = '$cat_id'");
$data3 = mysql_fetch_array($result3);
if (mysql_num_rows($result3) > 0){
if($counter == $parts_count-2){
return $data3['sub_cat_id'];
}
}
else {
$check = 0;

}
}
$counter = $counter+1;
}
}
}
?>


+ you have to include htaccess file. Well I need a feedback...

degsy
March 31st, 2007, 11:41 AM
What is the problem?

FrEaKmAn
March 31st, 2007, 12:22 PM
there is no problem, I'm just wondering what do think about my method of scripting... is there any way to improve it and make everything faster...

degsy
March 31st, 2007, 01:02 PM
What's this for?
for($i=0; $i<1000; $i++){
if($check == 0){
//return 'error';
break;
}


PHP has many different methods and functions to perform operations. People will always have a preferrer method.

e.g. I went for array_merge instead of adding to arrays using $a[]

If it works then it should be ok.

FrEaKmAn
March 31st, 2007, 01:24 PM
well that' a lousy looping... I'll try to resolve this