4

I've been fighting for hours with such a problem. For speeding my web-page I request database to get all categories just one time and then want to sort the array using PHP.

Array
(
    [0] => Array
        (
            [id] => 1
            [name] => name1
            [parent] => 0
            [children] => 
        )
   [1] => Array
    (
        [id] => 2
        [name] => name2
        [parent] => 1
        [children] => 
    )
)

I need to get something like this

Array
(
    [0] => Array
        (
            [id] => 1
            [name] => name1
            [parent] => 0
            [children] => Array
                  (
                      [id] => 2
                      [name] => name2
                      [parent] => 1
                      [children] => 
                  )
        )

)

The problem with that is making it for any level of hierarchy. So that it could work in cycle. Please help!

user597214
  • 41
  • 1
  • 3

5 Answers5

4

There are lots of solutions to reducing the overhead here. But without knowing what your constraints are, it's difficult to recommend an approach.

e.g.:

  1. use an adjacency model - see my comment on dnagirl's answer

  2. load all the data into PHP then use a recursion algorithm to create the nested tree (this will be rather slow and would benefit from some caching)

  3. write a recursive stored procedure which returns the result set sorted by a depth-first tree walk

Taking example 2 a bit closer to code....something like....

 function build_tree(&$unsorted, $start_node)
 {
     $out=array();
     foreach($unsorted as $key=>$node) {
        if ($node['parent']==$start_node) {
            $node['children']=build_tree($unsorted, $key);
            unset($unsorted[$key]);
            $out[]=$node;
        }
     }
     return $out;
 } 
 $threaded_tree=build_tree($list_of_nodes, 0); // assumes that the root is the first element
symcbean
  • 47,736
  • 6
  • 59
  • 94
0

Not sure if you've found an answer yet, but I was looking for the same solution today and finally ended up making my own solution. The below code is the class I just created and it works with PHP arrays and objects and is recursive to an unlimited number of dimensions.

GitHub https://github.com/DukeOfMarshall/PHP-Array-Heirarchy-Display

A simple example of using this code would be:

<?php

require_once('Arrays.class.php');
$display = new Array_Functions();

$display->display_hierarchy($multidimensional_array);

?>

There are other options that can be set as well, but that was just a straight up heirarchal display of an array or object.

<?php

class Array_Functions {
public $number_of_tabs  = 3; # The default number of tabs to use when branching out
private $counter            = 0; # The counter to use for the number of tab iterations to use on the current branch

public $display_square_brackets     = TRUE; 
public $display_squiggly_brackets = FALSE;
public $display_parenthesis           = FALSE;

public $display_apostrophe  = TRUE;
public $display_quotes        = FALSE;

public function __construct(){
}

/**
 * Displays the array in an organized heirarchy and even does so recursively
 * 
 * $array ARRAY - The array to display in a heirarchy
 * 
 * $key_bookends STRING - The character to place on either side of the array key when printed
 * @@ square - Displays a set of square brackets around the key (DEFAULT)
 * @@ squiggly - Displays a set of squiggly brackets around the key
 * @@ parenthesis - Displays a set of parenthesis around the key
 * @@ FALSE - Turns off the display of bookends around the array key
 * 
 * $key_padding STRING - The padding to use around the array key with printed
 * @@ quotes - Pads the array key with double quotes when printed
 * @@ apostrophe - Pads the array key with apostrophes when printed (DEFAULT)
 * @@ FALSE - Turns off the display of padding around the array key
 * 
 * $number_of_tabs_to_use INT - The number of tabs to use when a sub array within the array creates a branch in the heirarchy
 * 
 */
public function display_hierarchy($array, $key_bookends = '', $key_padding = '', $number_of_tabs_to_use = ''){
    # Convert the input to a JSON and then back to an array just to make sure we know what we're working with
    $array = $this->convert_object_to_array($array);

    # If the $array variable is still not an array, then error out.
    # We're not going to fool around with your stupidity
    if(gettype($array) != 'array'){
        echo 'Value submitted was '.strtoupper(gettype($array)).' instead of ARRAY or OBJECT. Only arrays or OBJECTS are allowed Terminating execution.';
        exit();
    }

    # Establish the bookend variables
    if($key_bookends != '' || !$key_bookends){
        if(strtolower($key_bookends) == 'square'){
            $this->display_square_brackets      = TRUE;
            $this->display_squiggly_brackets    = FALSE;
            $this->display_parenthesis            = FALSE;
        }elseif(strtolower($key_bookends) == 'squiggly'){
            $this->display_square_brackets      = TRUE;
            $this->display_squiggly_brackets    = TRUE;
            $this->display_parenthesis            = FALSE;
        }elseif(strtolower($key_bookends) == 'parenthesis'){
            $this->display_square_brackets      = FALSE;
            $this->display_squiggly_brackets    = FALSE;
            $this->display_parenthesis          = TRUE;
        }elseif(!$key_bookends){
            $this->display_square_brackets      = FALSE;
            $this->display_squiggly_brackets    = FALSE;
            $this->display_parenthesis            = FALSE;
        }
    }


    # Establish the padding variables
    if($key_padding != '' || !$key_padding){
        if(strtolower($key_padding) == 'apostrophe'){
            $this->display_apostrophe = TRUE;
            $this->display_quotes       = FALSE;
        }elseif(strtolower($key_padding) == 'quotes'){
            $this->display_apostrophe = FALSE;
            $this->display_quotes       = TRUE;
        }elseif(!$key_padding){
            $this->display_apostrophe = FALSE;
            $this->display_quotes       = FALSE;
        }
    }       

    # Establish variable for the number of tabs
    if(isset($number_of_tabs_to_use) && $number_of_tabs_to_use != ''){
        $this->number_of_tabs = $number_of_tabs_to_use;
    }

    foreach($array as $key => $value){
        $this->insert_tabs();

        if(is_array($value)){
            echo $this->display_padding($key)." => {<BR>";

            $this->counter++;
            $this->display_hierarchy($value);
            $this->counter--;
            $this->insert_tabs();
            echo '} <BR>';
        }else{
            echo $this->display_padding($key)." => ".$value.'<BR>';
        }
    }
}

# Inserts tab spaces for sub arrays when a sub array triggers a branch in the heirarchy
# Helps to make the display more human readable and easier to understand
private function insert_tabs(){
    for($i=1; $i<=$this->counter; $i++){
        for($x=1; $x<=$this->number_of_tabs; $x++){
            echo '&emsp;';
        }
    }
}

# Takes a PHP object and converts it to an array
# Works with single dimension and multidimensional arrays
public function convert_object_to_array($object){
    $object = json_decode(json_encode($object), TRUE);
    return $object;
}

# Sets the displayed padding around the array keys when printed on the screen
public function display_padding($value){
    $default_container = "['VALUE_TO_REPLACE']";

    $value = str_replace("VALUE_TO_REPLACE", $value, $default_container);

    if($this->display_square_brackets){
    }elseif($this->display_squiggly_brackets){
        $value = str_replace('[', '{', $value);
        $value = str_replace(']', '}', $value);
    }elseif($this->display_parenthesis){
        $value = str_replace('[', '(', $value);
        $value = str_replace(']', ')', $value);
    }else{
        $value = str_replace('[', '', $value);
        $value = str_replace(']', '', $value);
    }

    if($this->display_apostrophe){
    }elseif($this->display_quotes){
        $value = str_replace("'", '"', $value);
    }else{
        $value = str_replace("'", '', $value);
    }

    return $value;
}
}

?>
0
foreach ($dataFromMySQL as $e) {
    $all[$e['id']] = $e;
    $ref = &$all[$e['id'];
    if ($e['parent']) $all[$e['id']]['children'][] = $ref;
    else $hierarchy[$e['id']] = $ref;
}

This expects parents to always be populated before its children

Mikhail
  • 8,692
  • 8
  • 56
  • 82
  • Like what db structure for this should I have to fit it best? – user597214 Jan 31 '11 at 17:30
  • I thought you already had a db structure... the structure you got is fine. Depending on the information that you're storing/displaying @dnagirl may be right... Ajax may be better. if you need all of the information at once - use the database structure that you've proposed with population method that I wrote above. – Mikhail Jan 31 '11 at 17:38
0

You're using an adjacency model for your hierarchy. You will either have to query your db multiple times, or if the number of levels is known, construct a union query with a clause for each level.

If it is allowed, you'll likely find that an AJAX solution where child levels are called when the parent is clicked is both easier to program and more efficient.

dnagirl
  • 20,196
  • 13
  • 80
  • 123
  • so what the best way to store hierarchy in db for not to have such problem& – user597214 Jan 31 '11 at 17:29
  • actually that's parent referencing - not adjacency - see http://articles.sitepoint.com/article/hierarchical-data-database for a description of an adjacency model. – symcbean Jan 31 '11 at 17:29
  • @user597214: an excellent reference for learning about how to model hierarchies in SQL is Joe Celko's *Trees and Hierarchies in SQL for Smarties*. The most appropriate model to use depends on a number of things; one of the most important is how often the data in the tree will change. – dnagirl Jan 31 '11 at 17:39
  • 1
    @symcbean What's the difference? – Wiseguy Jan 31 '11 at 20:16
0

What you are looking for is called recursion, as for me it is not very clear how to handle recursions in php and documentation is almost blank regarding this subject.

You might be interested to check this and this article

Nazariy
  • 6,028
  • 5
  • 37
  • 61
  • 1
    recursion in PHP is the same as in most other similar languages. Call the function from itself and make sure you have a stop condition somewhere in the function. – dnagirl Jan 31 '11 at 18:11