views:

589

answers:

1

This working code seems to be the typical solution to this problem.

It takes a multi-dimensional array that holds categories and their subcategories (with no implied limitation on how many levels deep it goes) and creates an HTML unordered list from it, echoing it out onto the page from inside a recursive function.

Sub-levels are traversed by passing the value for each array element's 'children' key to array_walk() recursively, from inside the original callback function named _category_list()_.

How can this method of output be modified so that all HTML code would exist in the template, outside the function?

Here's the rundown of the code:

This multi-dimensional array holds the multi-level category tree.

Important keys to use in the HTML are 'category_id', 'name', and 'children'. Other keys have been purged from the array below for simplicity sake, but if they are useful they are: 'parent_id' and 'level' (starting with level 1).

<?php

// the array containing the tree
$categories = array (
  'category_id' => '2',
  'name' => 'Top Category Name',
  'children' => array (
    0 => array (
      'category_id' => '188',
      'name' => 'Category Name',
      'children' => array (
        0 => array (
          'category_id' => '159',
          'name' => 'Category Name',
          'children' => array (),
        ),
        1 => array (
          'category_id' => '160',
          'name' => 'Category Name',
          'children' => array (),
        ),
        2 => array (
          'category_id' => '166',
          'name' => 'Category Name',
          'children' => array (),
        ),
      ),
    ),
    1 => array (
      'category_id' => '4',
      'name' => 'Category Name',
      'children' => array (
        0 => array (
          'category_id' => '141',
          'name' => 'Category Name',
          'children' => array (),
        ),
        1 => array (
          'category_id' => '142',
          'name' => 'Category Name',
          'children' => array (),
        ),
      ),
    ),
  ),
)

?>

.

This next function produces the majority of the HTML output, but it locks the HTML inside itself.

However, instead of echoing it right from the function, I'm looking for a way to pass this data back to the view template in a manner that is friendly for designers to customize.

<?php

// separate the HTML from this function,
// passing $v to the view template for handling
function category_list($v, $k){
  switch ($k) {
    case 'category_id':
      echo "<li id="$v">";
      break;
    case 'name':
      echo "$v";
      break;
    case 'children':
      if(count($v) > 0){
        echo "<ul>";
        foreach($v as $k=>$v)
        array_walk($v, 'category_list');
        echo "</ul>";
      }
      echo "</li>";
      break;
  }
}

?>

.

The next block of code is the current template html/php with the call to traverse the first level of the array via array_walk() and referencing the recursive function above. The function itself then handles the recursion and iteration of deeper categories with 1 or more child. Of course, this is the typical approach.

This code should have all HTML tags, rather than just the outer tags.

<ul>
<?php array_walk($tree,'category_list'); ?>
</ul>

.

The Ideal Solution:

The end goal here is to find a way for template designers to create their ideal navigation structure without having to create or modify the recursion function (which isn't accessible), nor require use of a foreach loop for each level of the multi-dimensional array. The solution should not be tied to any specific depth limitations.

Examples of HTML customizations could range from placing additional attributes inside the ul/li tags, or even wrapping new tags around the output text, such as span tags, which are commonly used in navigations to achieve a sliding-doors effect with CSS. So I think the appropriate solution will need to support those case scenarios at a minimum.

Iterating through the array from the template using array_walk() would still be okay, as long as it can be used in such a way that the callback function passes the desired vars back to the template for use with the designer's HTML.

Ideally, if array_walk_recursive() knew how many levels deep its iterator actually is, I think this feat would be much easier to solve. But unless someone knows a workaround to that issue, the solution may be different entirely.

I also want to avoid using javascript methods of building the tree. And if there's a way to avoid using the switch, I'm open to suggestions there too.

+1  A: 

Have you thought of writing a Class that manages and stores the info you want to pass back. You function could alter an instance of that class as it goes though and in the end pass back the filled object.

Your content would be encapsulated inside the class and you can write all the methods and utilities for the user to manipulate and output the data. Utility methods can also be written inside the class to show the number of levels, etc.

I haven't tried it myself but this is where I would start since the Class allows me to refine and expand on what my users would want and they wouldn't need to know the internal details of what was happening.

Paulo
The function in my example is actually a method of a catalog class that handles php calls from the template. But I wanted to keep the example simple, so I didn't mention the existing object-oriented structure. But, that's the reason HTML needs to be kept out of the function.So within that context, I'm not sure how I would apply your concept. Were you thinking of something like a helper class?