SitePoint Sponsor |
|
User Tag List
Results 1 to 6 of 6
Thread: Displaying a Multilevel Tree
-
May 11, 2009, 09:45 #1
- Join Date
- Dec 2005
- Posts
- 336
- Mentioned
- 1 Post(s)
- Tagged
- 0 Thread(s)
Displaying a Multilevel Tree
Thanks to Rudy Limeback's tutorial about Categories and Subcategories and the Simply SQL book, I am writing better sql and reducing my queries in my scripts...
But I am having an issue with displaying this format using Rudy's first example from Displaying all categories and subcategories: site maps and navigation bars
My query gives me this
animal birdie NULL NULL
animal doggie companion chihuahua
animal doggie companion poodle
And I need PHP to output
animal
- birdie
- doggie
-- companion
--- chihuahua
--- poodle
But it gives me
animal
animal
- birdie
animal
- doggie
etc etc
Any tips on how make this happen the correct way (and faster, my code is very bloated..)?
-
May 11, 2009, 10:28 #2
- Join Date
- Jul 2006
- Location
- Augusta, Georgia, United States
- Posts
- 4,194
- Mentioned
- 17 Post(s)
- Tagged
- 5 Thread(s)
First you need to build the tree.
PHP Code:<?php
class Animal {
protected $animals;
protected $name;
public function __construct($name) {
$this->animals = array();
$this->name = $name;
}
public function matches($pk) {
return strcasecmp($pk,$this->getName())==0;
}
public function getName() {
return $this->name;
}
public function addAnimal(Animal $animal) {
$this->animals[] = $animal;
}
public function getAnimals() {
return $this->animals();
}
public function hasAnimals() {
return empty($this->animals)?false:true;
}
public function hasAnimalByName($name) {
foreach($this->animals as $animal) {
if($animal->matches($name)==true) {
return true;
}
}
return false;
}
public function getAnimalByName($name) {
foreach($this->animals as $animal) {
if($animal->matches($name)==true) {
return $animal;
}
}
}
public function addRow($row,$runner=1) {
if(!array_key_exists($runner,$row)) return;
if(is_null($row[$runner])) return;
if($this->hasAnimals()===true && $this->hasAnimalByName($row[$runner])===true) {
$childAnimal = $this->getAnimalByName($row[$runner]);
} else {
$childAnimal = new Animal($row[$runner]);
$this->addAnimal($childAnimal);
}
$childAnimal->addRow($row,($runner+1));
}
}
$animals = array();
$result = array(
array('animal','birdie',null,null)
,array('animal','doggie','companion','chihuahua')
,array('animal','doggie','companion','poodle')
);
foreach($result as $row) {
$currentRootAnimal = null;
if(!empty($animals)) {
foreach($animals as $animal) {
if($animal->matches($row[0])===true) {
$currentRootAnimal = $animal;
break;
}
}
} else {
$currentRootAnimal = new Animal($row[0]);
$animals[] = $currentRootAnimal;
}
$currentRootAnimal->addRow($row,1);
}
echo '<pre>',print_r($animals),'</pre>';
?>
-
May 11, 2009, 10:39 #3
- Join Date
- Jul 2006
- Location
- Augusta, Georgia, United States
- Posts
- 4,194
- Mentioned
- 17 Post(s)
- Tagged
- 5 Thread(s)
Actually this more like what you would want so that you can map each depth to the appropriate row key in the result set.
PHP Code:<?php
class Animal {
protected $animals;
protected $name;
public function __construct($name) {
$this->animals = array();
$this->name = $name;
}
public function matches($pk) {
return strcasecmp($pk,$this->getName())==0;
}
public function getName() {
return $this->name;
}
public function addAnimal(Animal $animal) {
$this->animals[] = $animal;
}
public function getAnimals() {
return $this->animals();
}
public function hasAnimals() {
return empty($this->animals)?false:true;
}
public function hasAnimalByName($name) {
foreach($this->animals as $animal) {
if($animal->matches($name)==true) {
return true;
}
}
return false;
}
public function getAnimalByName($name) {
foreach($this->animals as $animal) {
if($animal->matches($name)==true) {
return $animal;
}
}
}
public function addRow($row,$map,$runner=0) {
if(!array_key_exists($runner,$map)) return;
$key = $map[$runner];
if(!array_key_exists($key,$row)) return;
if(is_null($row[$key])) return;
if($this->hasAnimals()===true && $this->hasAnimalByName($row[$key])===true) {
$childAnimal = $this->getAnimalByName($row[$key]);
} else {
$childAnimal = new Animal($row[$key]);
$this->addAnimal($childAnimal);
}
$childAnimal->addRow($row,$map,($runner+1));
}
}
$animals = array();
$result = array(
array('animal','birdie',null,null)
,array('animal','doggie','companion','chihuahua')
,array('animal','doggie','companion','poodle')
);
foreach($result as $row) {
$currentRootAnimal = null;
if(!empty($animals)) {
foreach($animals as $animal) {
if($animal->matches($row[0])===true) {
$currentRootAnimal = $animal;
break;
}
}
} else {
$currentRootAnimal = new Animal($row[0]);
$animals[] = $currentRootAnimal;
}
$currentRootAnimal->addRow($row,array(1,2,3));
}
echo '<pre>',print_r($animals),'</pre>';
?>
-
May 11, 2009, 10:49 #4
- Join Date
- Jul 2006
- Location
- Augusta, Georgia, United States
- Posts
- 4,194
- Mentioned
- 17 Post(s)
- Tagged
- 5 Thread(s)
Will build the tree and make the menu.
PHP Code:<?php
class Animal {
protected $animals;
protected $name;
public function __construct($name) {
$this->animals = array();
$this->name = $name;
}
public function matches($pk) {
return strcasecmp($pk,$this->getName())==0;
}
public function getName() {
return $this->name;
}
public function addAnimal(Animal $animal) {
$this->animals[] = $animal;
}
public function getAnimals() {
return $this->animals;
}
public function hasAnimals() {
return empty($this->animals)?false:true;
}
public function hasAnimalByName($name) {
foreach($this->animals as $animal) {
if($animal->matches($name)==true) {
return true;
}
}
return false;
}
public function getAnimalByName($name) {
foreach($this->animals as $animal) {
if($animal->matches($name)==true) {
return $animal;
}
}
}
public function addRow($row,$map,$runner=0) {
if(!array_key_exists($runner,$map)) return;
$key = $map[$runner];
if(!array_key_exists($key,$row)) return;
if(is_null($row[$key])) return;
if($this->hasAnimals()===true && $this->hasAnimalByName($row[$key])===true) {
$childAnimal = $this->getAnimalByName($row[$key]);
} else {
$childAnimal = new Animal($row[$key]);
$this->addAnimal($childAnimal);
}
$childAnimal->addRow($row,$map,($runner+1));
}
public function buildMenu() {
if($this->hasAnimals()===true) {
echo '<ul>',"\n";
foreach($this->getAnimals() as $item) {
if($item->hasAnimals()===true) {
echo '<li>',$item->getName();
$item->buildMenu();
echo '</li>',"\n";
} else {
echo '<li>',$item->getName(),'</li>',"\n";
}
}
echo '</ul>',"\n";
}
}
}
$animals = array();
$result = array(
array('animal','birdie',null,null)
,array('animal','doggie','companion','chihuahua')
,array('animal','doggie','companion','poodle')
);
foreach($result as $row) {
$currentRootAnimal = null;
if(!empty($animals)) {
foreach($animals as $animal) {
if($animal->matches($row[0])===true) {
$currentRootAnimal = $animal;
break;
}
}
} else {
$currentRootAnimal = new Animal($row[0]);
$animals[] = $currentRootAnimal;
}
$currentRootAnimal->addRow($row,array(1,2,3));
}
if(!empty($animals)) {
echo '<ul id="animal-menu">';
foreach($animals as $animal) {
if($animal->hasAnimals()===true) {
echo '<li>',$animal->getName();
$animal->buildMenu();
echo '</li>',"\n";
} else {
}
}
echo '</ul>';
}
?>
-
May 11, 2009, 11:35 #5
- Join Date
- Jul 2008
- Posts
- 5,757
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
This is less flexible than what oddz posted, but I'm posting it in hopes it may help you understand the logic better. Hopefully you have a grasp of recursion, as this makes heavy use of it.
PHP Code:// transforms array(1, 2, 3) to array(1 => array(2 => array(3 => array()))
// stops when null is encountered
function flatarray_to_heiarchy($array) {
$node = array_shift($array);
return is_null($node) ? array() : array($node => flatarray_to_heiarchy($array));
}
function to_html_tree($array) {
$ul_content = '';
foreach ($array as $name => $children) {
$more_ul_content = count($children) ? to_html_tree($children) : '';
$ul_content .= "<li>$name $more_ul_content</li>";
}
return "<ul>$ul_content</ul>";
}
PHP Code:$tree = array();
while ($row = mysql_fetch_assoc($result)) {
$tree = array_merge_recursive($tree, flatarray_to_heiarchy($row));
}
// print_r($tree);
echo to_html_tree($tree);
-
May 11, 2009, 17:51 #6
- Join Date
- Dec 2005
- Posts
- 336
- Mentioned
- 1 Post(s)
- Tagged
- 0 Thread(s)
oddz, crmalibu,
Thank you both for the code ideas.
To note, I will be pull more information than just the name
Oddz, your code went way over my head and testing it out didn't do to well.
Crmalibu, testing your code worked for just the name, but adding other stuff severly broke the output.
My query that I will be using (toned it down and at a 2 levels)
$query = "
select
r.id as r_id, r.name as r_name, r.uri as r_uri,
r.date as r_date, r.status as r_status, r.entry_order as r_order,
l1.id as l1_id, l1.name as l1_name, l1.uri as l1_uri,
l1.date as l1_date, l1.status as l1_status, l1.entry_order as l1_order
from entries as r
left outer join entries as l1
on l1.parent_id = r.id
where r.parent_id isnull
and r.type = 'page'
order by
r.entry_order,
l1.entry_order
";
Array
(
[r_id] => 1
[r_name] => Home
[r_uri] =>
[r_date] => 2009-05-10 04:10:02
[r_status] => 1
[r_order] => 0
[l1_id] =>
[l1_name] =>
[l1_uri] =>
[l1_date] =>
[l1_status] =>
[l1_order] =>
)
Array
(
[r_id] => 3
[r_name] => TestPage
[r_uri] => testpage
[r_date] => 2009-05-10 04:10:02
[r_status] => 1
[r_order] => 0
[l1_id] => 4
[l1_name] => subPage
[l1_uri] => subpage
[l1_date] => 2009-05-10 04:10:02
[l1_status] => 1
[l1_order] => 0
)
Array
(
[r_id] => 3
[r_name] => TestPage
[r_uri] => testpage
[r_date] => 2009-05-10 04:10:02
[r_status] => 1
[r_order] => 0
[l1_id] => 7
[l1_name] => SubTwo
[l1_uri] => subtwo
[l1_date] =>
[l1_status] => 1
[l1_order] => 1
)
Bookmarks