Building a Parent/Child Category Structure

By Hawkee on Aug 20, 2004

This generates two structures: a tree and a list of objects. Both are generated from the following standard parent/child database format:

  • cat_id: primary key, auto_increment
  • cat_title: name of category
  • cat_parent_id: points to the cat_id of this items parent.

These are two recursive functions that first build a tree structure. The second recursive function builds a sortable list of objects with their full category path as shown below:
For Cat3: Cat1 > Cat2 > Cat3

It's helpful to use a print_r on both objects to get a visual idea of what you've built.

Important Notes:

  • This code will not work without a query and a database.
  • The parent category needs to be included in the dataset and $directory_id needs to match it's cat_id.
/************************************************************/
// Category Item class

class category_item
{
    var $cat_id;
    var $depth;
    var $cat_title;
    var $cat_parent_id;
    var $cat_long_title;

    function category_item($cat_id, $depth, $cat_title, $cat_parent_id, $cat_long_title = '')
    {
        $this->cat_id = $cat_id;
        $this->depth = $depth;
        $this->cat_title = $cat_title;
        $this->cat_parent_id = $cat_parent_id;
        $this->cat_long_title = $cat_long_title;
    }
}

/*************************************************************/
// Builds the category list for the category select

$list = array();

function build_list($cat_array, $item = '', $depth = 0)
{
    global $template;
    global $list;

    foreach($cat_array as $category)
    {
        $loop_item = "$item$category[cat_title]";

        $category_item = new category_item($category[cat_id], $depth, $category[cat_title], $category[cat_parent_id], $loop_item);
        $list[] = $category_item;

        if(count($category[children]) > 0)
        {
            $depth++;
            build_list($category[children], $loop_item." > ", $depth);
            $depth--;
        }

        $loop_item = '';
    }

    return $list;
}

/*************************************************************/
// Adds the object to the children array of the object with 
// a cat_id equal to $parent_id

function tree_add($tree, $parent_id, $object, $cat_id)
{
    // Only start from the given cat_id, ignore all other roots

    if($parent_id == '0' and $object[cat_id] == $cat_id)
    {
        $tree[$object[cat_id]] = $object;
        return $tree;
    }

    if($tree)
    {
        foreach($tree as $key => $value)
        {
            $current = $tree[$key];

            // If this is the parent, add the object to it's children array
            if($current[cat_id] == $parent_id)
            {
                $tree[$key][children][$object[cat_id]] = $object;
            }
            else
            {
                // If it's not in this level, look a level deeper on the current object.
                $tree[$key][children] = tree_add($current[children], $parent_id, $object, $cat_id);
            }
        }
    }

    return $tree;
}

/*************************************************************/
// This pulls the data from the database.  This will not work without a
// database connection.  This code is compatible with the phpBB2 
// mySQL database class.
//

$query = "
    select
        *
    from
        categories
    order by
        cat_id
";

$result = $db->sql_query($query);

while($category = $db->sql_fetchrow($result))
{
    $children = array();

    $category[children] = $children;

    $cat_id = $category[cat_id];
    $cat_parent_id = $category[cat_parent_id];

    $cat_tree = tree_add($cat_tree, $cat_parent_id, $category, $cat_id);
}

$cat_list = build_list($cat_tree);

Comments

Sign in to comment.
Koolvin   -  Oct 05, 2010

Can you give an example of a SQL table and the built tree?

 Respond  
serdominik   -  Oct 05, 2010

I'm sorry but this morning a fresh mind I found the solution!

here's the code:

<?php
$categorie=array();

$x=0;
foreach($cat_list as $cat => $value) {
foreach($value as $c => $v) {
$categorie[$x][$c]=$v;
}
$x++;
}
?>
and then a simple loop to the last array

thanks for this code wonderufl

 Respond  
serdominik   -  Oct 05, 2010

I know you!
but I need to use the array like this: $cat_list[$x]['cat_title'] or $cat_list[$x]['cat_id'] for a cycle to be applied to select html option or screen printing.
I tried the solution of a double foreach, but I can not apply this example:$cat_list[$x]['cat_title'] or $cat_list[$x]['cat_id'].
can you please post a valid method? I'm really in trouble!
thanks for your time

 Respond  
Hawkee   -  Oct 04, 2010

You can just loop through $cat_list which is an array of categories.

 Respond  
serdominik   -  Oct 04, 2010

how can I print my values?
an alternative to print_r?

 Respond  
Hawkee   -  May 02, 2010

lbss, you'll probably have to modify build_list to do ul and li rather than the Level1 > Level2 > Leve3 format. It might require some trial and error, but it can certainly be done to fit your format.

 Respond  
lbss   -  May 02, 2010

i have ommitted by db information above. you will have to fill in your own info for the vars below

$dbhost = "";
$dbuname = "";
$dbpword = "";
$dbname = "";

 Respond  
lbss   -  May 02, 2010

I realise its a while since the last post but i got it working fairly easily.

I replaced -

"$query = "
select
*
from
categories
order by
cat_id
";

$result = $db->sql_query($query);

while($category = $db->sql_fetchrow($result))
{
"
with -

"$query = "
select
*
from
categories
order by
cat_id
";

$result = mysql_query($query);

while($category = @mysql_fetch_assoc($result))
{
"

My db connection is as follows;

"$dbhost = "";
$dbuname = "";
$dbpword = "";
$dbname = "";

$client_flags = 128;
$dbconnection = mysql_connect($dbhost, $dbuname, $dbpword, FALSE, $client_flags) or die("Connection Failure to Database");
mysql_select_db($dbname, $dbconnection) or die ("Database not found.");
"

I can then print_r on both arrays an get the following

print_r($cat_list);

with my sample data outputs:

Array ( [0] => category_item Object ( [cat_id] => 32 [depth] => 0 [cat_title] => Home Page (sample) [cat_parent_id] => 0 [cat_long_title] => Home Page (sample) ) [1] => category_item Object ( [cat_id] => 33 [depth] => 1 [cat_title] => test [cat_parent_id] => 32 [cat_long_title] => Home Page (sample) > test ) [2] => category_item Object ( [cat_id] => 34 [depth] => 0 [cat_title] => test2 [cat_parent_id] => 0 [cat_long_title] => test2 ) )

and print_r($cat_tree);

with my sample data outputs:

Array ( [32] => Array ( [cat_id] => 32 [cat_parent_id] => 0 [cat_title] => Home Page (sample) [home] => 0 [children] => Array ( [33] => Array ( [cat_id] => 33 [cat_parent_id] => 32 [cat_title] => test [home] => 0 [children] => Array ( ) ) ) ) [34] => Array ( [cat_id] => 34 [cat_parent_id] => 0 [cat_title] => test2 [home] => 0 [children] => Array ( ) ) )

What id like to know is how can I translate this into an

tree style menu and a 'ROOT / Home Page (sample) / test' breadcrumb syle list ?
 Respond  
Hawkee   -  Aug 30, 2009

fahad, what problems did you have? Maybe I can help.

 Respond  
fahad_se   -  Aug 29, 2009

no success, just time wasted

 Respond  
Hawkee   -  Dec 05, 2008

Thanks for the feedback ejgeske. I realize it's a bit confusing and difficult to implement. I wrote this years ago using the phpBB2 database class, so that explains the $db->sql_fetchrow. You have to download phpBB2 and grab the db/mysql.php and includes/db.php files to instigate the $db class. Or you could modify all the $db calls to work with your favorite database class.

There is only one table in this example. The "cat_long_title" doesn't even get used and isn't necessary at all. I'm not even sure why it made it into this snippet. The table only needs the following fields:

cat_id
cat_title
cat_parent_id

The depth is calculated in the code.

I hope this clears up any confusion and let me know if you have any luck with it.

 Respond  
Jonesy44   -  Dec 05, 2008

righty.. i'll have a look into it. so basically $this->variable sets a string for ONLY that function?

 Respond  
ejgeske   -  Dec 05, 2008

on the say again, only the variable that was assigned with $this is assigned to the class for later use by all of the functions.

this is object oriented php which became available with php5

 Respond  
ejgeske   -  Dec 05, 2008

in the following example, i will assign some variables that belong to a class. But are assigned inside one of the function/methods. If you only assign it as $example and not as $this->example, it does not assign that variable to the entire class, only in that method/function.

class myClass
{
var $example;
var $example2;

function say_what()
{
    $example = 'Hi';
    $this->example2 = 'Hi again';

    echo '$example: '.$example."<br/>";
    echo '$example2: '.$example2."<br/>";
    echo '$this->example: '.$this->example."<br/>";
    echo '$this->example2: '.$this->example2."<br/>";
}
function say_again()
{
    echo '$example: '.$example."<br/>";
    echo '$example2: '.$example2."<br/>";
    echo '$this->example: '.$this->example."<br/>";
    echo '$this->example2: '.$this->example2."<br/>";
}

}

$convo = new myClass();
$convo->say_what();
$convo->say_again();


PRINT OUT

// this is $convo->say_what();
$example: Hi
$example2: //blank
$this->example: //blank
$this->example2: Hi again

// this is $convo->say_again();
$example: //blank
$example2: //blank
$this->example: //blank
$this->example2: Hi again // only this one assigned to class shows.

 Respond  
ejgeske   -  Dec 05, 2008

$this-> refers to the variables in the class ( "$this" means inside this or belongs to this object)

 Respond  
Jonesy44   -  Dec 05, 2008

$this->cat_id = $cat_id;

What does $this refer to?
And what the hell are "->"'s?

 Respond  
ejgeske   -  Dec 05, 2008

It would be very helpful it you could add the database layouts.

Also, you only reference one database but you state we need to pull information from two.

In your class you state:
var $cat_id;
var $depth;
var $cat_title;
var $cat_parent_id;
var $cat_long_title;

It is hard to know which tables these belong to.
Also, in your $db->fetchrow(), is this a mysql_fetch_row, or mysql_fetch_object, assoc? Assuming you said row, im guessing it would be a fetch row, but clarification would be nice. Also, maybe a working script.

 Respond  
Hawkee   -  Aug 26, 2008

Not necessarily. I added a field for the number of products in each category, but you'll have to create a script to calculate that number on a daily basis. Of course it's always important to have date, time and timestamp so you know when the category was created and last edited.

 Respond  
lostdeviant   -  Aug 26, 2008

Hawkee, Do you recommend any additional columns with the three you mentioned above for the "categories" table?

I'll be using categories with "breadcrumb navigation" on my sites. (at least I hope to)

 Respond  
F*U*R*B*Y*   -  Mar 20, 2007

can you explain what this does please :) i don\'t really understand what you mean parent/child category structure

 Respond  
Hawkee   -  Mar 19, 2007

rompers, what are you asking?

 Respond  
rompers   -  Mar 18, 2007

hello !

as now sql code should look

sorry for my bad english

thxRompers

 Respond  
Hawkee   -  Mar 22, 2006

It seems like your $cat_tree wasn\'t created properly. Make sure you check the data you\'re pulling from mySQL to be sure it\'s got a cat_title, cat_id and cat_parent_id for each category in your database.

 Respond  
Pauladao   -  Mar 22, 2006

Hi Hawkee! This code is very usefull, but i\'m getting an error while running it:

Warning: Invalid argument supplied for foreach() in c:\web\anuncios\admin\dump.php on line 35

Dump.php is what I named the script, the line with the error is:
foreach($cat_array as $category)

I tried to do something similar to this script myself, but I was not sucessfull. If you can see this and help figuring this error out, I would appreciate very much.

Best Regards;

(Marcio)

 Respond  
Are you sure you want to unfollow this person?
Are you sure you want to delete this?
Click "Unsubscribe" to stop receiving notices pertaining to this post.
Click "Subscribe" to resume notices pertaining to this post.