More recursive madness

#1
[eluser]skunkbad[/eluser]
A couple of days ago I was working on a recursive function that builds the category menu for my ecommerce application. I finished that with code handed to me by Tominator, and then I went on to work on the way that the new URLs were being handled by the product controller / model, so that the appropriate category would show products or sub-categories, even if more than one category had the same name.

So last night I finished up on yet another recursive function. This time I got it right, but I'm wondering if it couldn't be simplified. Since I'm not so good with recursive functions, perhaps you could offer advice.

I'm just going to post a link to the file on Bitbucket:

http://bitbucket.org/skunkbad/community-..._model.php

I'm specifically looking at lines 80 through 157, which includes two methods:

1) get_category_steps()
2) get_category_parents()

ALSO, The make_category_menu() method in the same file is not recursive. It only goes 7 levels deep, which I thought would be enough for most stores, but if somebody has a truly recursive way to do what I need done here, I would love to see the solution.

Any help is greatly appreciated, and you can receive credit in the code for your help if you wish.

#2
[eluser]Federico Baña[/eluser]
Try something like this for the cateogry menu:
Code:
// Lets see..
/* here you'll store the categories with its children like this:
"0" = > [
   "{cat_id}" => [
                 "name" => "{cat_name}",
                 "children" => [
                                  "{cat_id}" => [
                                                    ...
                                                ],
                                  "{cat_id}" => [
                                                    ...
                                                ]
                               ]
                 ],
   "{cat_id}" => [
                 "name" => "{cat_name}",
                 "children" => [
                                  "{cat_id}" => [
                                                    ...
                                                ],
                                  "{cat_id}" => [
                                                    ...
                                                ]
                               ]
                 ]
]
*/
//
public $category_tree = array();

// this function will generate that list with the array you have now
// always considering you have the cats sorted by parent_id ASC
function generate_menu()
{
     // define a root category with id 0 and store all the non-children cats inside
     $root = array('children' => array());
     $this->category_tree["0"] = $root;
     // then go trough all the items in the array
     foreach($this->categories as $c)
     {
          // and just add the current category as a child of its parent
          // id the main array is sorted by parent_id asc, at the time of adding children categories we'll already have all the "root" categories stored inside our root array
          $this->category_tree[ $c['parent_id'] ]['children'][] = $c;
     }
     // then remove the first level of the array as its useless from now on
     $this->category_tree = $this->category_tree["0"];
}

hope this helps..
about the other recursives i cant figure out what you want to get there, what is $segs actually? (in the first method)

#3
[eluser]Mischievous[/eluser]
I would need to see your db setup but I have a couple functions that do this. That allow an unlimited amount of levels using 1 db request.

#4
[eluser]skunkbad[/eluser]
[quote author="Mischievous" date="1271830145"]I would need to see your db setup but I have a couple functions that do this. That allow an unlimited amount of levels using 1 db request.[/quote]

The DB is like this:
http://bitbucket.org/skunkbad/community-...y_cart.sql

Currently, the constructor brings in all category table data and creates a result array. That array is used by a a lot of the methods, so I need to use that array to build the menu. There is a recursive function in the category_menu view that takes what the make_category_menu() method passes to it and creates the menu. Maybe there is a more direct way, or a more simple way. Like I said earlier, it does work, but if there was some optimization or simplification, everyone that uses the application would benefit.

#5
[eluser]skunkbad[/eluser]
Probably the best way to approach what I am trying to do is simply show the result array, and then the processed/sorted array.

This is the result array before sorting:

Code:
Array
(
    [0] => Array
        (
            [category_id] => 1
            [category_name] => Examples
            [parent_id] => 0
        )

    [1] => Array
        (
            [category_id] => 6
            [category_name] => Something
            [parent_id] => 0
        )

    [2] => Array
        (
            [category_id] => 2
            [category_name] => Widgets
            [parent_id] => 1
        )

    [3] => Array
        (
            [category_id] => 3
            [category_name] => Little
            [parent_id] => 1
        )

    [4] => Array
        (
            [category_id] => 7
            [category_name] => Yaknow
            [parent_id] => 1
        )

    [5] => Array
        (
            [category_id] => 4
            [category_name] => Hardcore
            [parent_id] => 2
        )

    [6] => Array
        (
            [category_id] => 8
            [category_name] => Whatever
            [parent_id] => 2
        )

    [7] => Array
        (
            [category_id] => 11
            [category_name] => Hardcore
            [parent_id] => 3
        )

)

This is the array after sorting:

Code:
Array
(
    [Examples] => Array
        (
            [category_id] => 1
            [sub] => Array
                (
                    [Widgets] => Array
                        (
                            [category_id] => 2
                            [sub] => Array
                                (
                                    [Hardcore] => Array
                                        (
                                            [category_id] => 4
                                        )

                                    [Whatever] => Array
                                        (
                                            [category_id] => 8
                                        )

                                )

                        )

                    [Little] => Array
                        (
                            [category_id] => 3
                            [sub] => Array
                                (
                                    [Hardcore] => Array
                                        (
                                            [category_id] => 11
                                        )

                                )

                        )

                    [Yaknow] => Array
                        (
                            [category_id] => 7
                        )

                )

        )

    [Something] => Array
        (
            [category_id] => 6
        )

)

This array goes into the view where the category menu is built with yet another recursive function. I am probably just overworking this....

#6
[eluser]Mischievous[/eluser]
Builders:
Code:
var $locations =  array();
var $map = array();
var $count = 0;
function mapKey($keyterm, $array)
    {
        foreach($array as $key => $value)
        {
            if ($keyterm == $key)
            {
                $this->locations[] = $key;
                return TRUE;
            }
           if(is_array($value))
           {
                if($this->mapKey($keyterm, $value) == TRUE)
                {
                    $this->locations[] = $key;
                    return TRUE;
                } else {
                     continue;
                }
          }
           }
    }
function returnKeyValue($key, $array)
    {
        unset($this->locations);
        if($this->mapKey($key, $array) == TRUE){
            $result = array_reverse($this->locations);
            //$result = array_slice($result, 0, count($this->locations)-1);
            return $result;
        } else {
            return "FALSE";
        }
    }
function buildTree($id_field, $parentid_field, $child_field, $array)
    {
        $final = array();
        $temparray = array();
        foreach($array as $arr)
        {
            if($arr[$parentid_field] == 0)
            {
                $final[$arr[$id_field]] = $arr;
            } else {
                if($arr[$child_field] == 1)
                {
                    $temparray[$arr[$parentid_field]][$arr[$id_field]] = $arr;
                } else {
                    $temparray[$arr[$parentid_field]][$arr[$id_field]] = $arr;
                }
            }
        }
        foreach($temparray as $key => $value)
        {
            $map = $this->returnKeyValue($key, $final);
            $mapcount = count($map);
            if(is_array($map))
            {
                $string = "final";
                for($i = 0; $i < $mapcount; $i++)
                {
                        if(is_int($map[$i]))
                        {
                            $string .= "[".$map[$i]."]";
                        } else {
                            $string .= "['".$map[$i]."']";
                        }
                }
                $string .= "['children']";
                eval("\$$string = \$value;");
            }
        }
        return $final;
    }

Display[ers]
Code:
function formatMenu($menu)
    {
          foreach($menu as $m)
        {
            if($m['mi_published'] == 1)
            {
            $this->string .= "<li class='".$m['css_class']."'>";
            $link = "<a >config->slash_item('base_url').$m['href']."' title='".$m['name']."'>".$m['name']."</a>";
            if($this->use_wrapper)
            {
                if($m['parent'] == 0)
                {
                    if(is_array($this->wrapper))
                    {
                        $this->string .= $this->wrapper[0].$link.$this->wrapper[1];
                    } else {
                        $this->string .= $link;
                    }
                } else {
                    $this->string .= $link;
                }
            } else {
                $this->string .= $link;
            }
            if(array_key_exists('children', $m))
            {
                if(is_array($m['children']))
                {
                    $this->string .= "<ul>";
                    $this->formatMenu($m['children']);
                    $this->string .= "</ul>";
                }
            }
            $this->string .= "</li>";
            }
        }
    }
Use
Code:
$menu_items = $this->grabMenu($result->row('id'));
$request = $this->AM->buildTree('id','parent','has_sub', $menu_items);
$this->formatMenu($request);

There is quite a bit going on but what is nice is that the "builders" i set in an array model that handles arrays and all i have to do with any multi-level array i have to build is call out to the array_model. The builder is just a iterative function while the display function is recursive... in this instance i have a a wrapper that can be set but i didnt include that function. Hope this helps out.

#7
[eluser]skunkbad[/eluser]
I came up with what will be the best solution for my needs. I studied other recursive menus, including my own that are working, and this was the answer:

Code:
&lt;?php

// Function for displaying a list.
// Receives one argument: an array.
function make_list ($child, $parents='')
{
    // Need the main $categories array:
    global $categories;

    // Start a list:
    echo '<ul>';

    // Loop through each subarray:
    foreach ($child as $cat_id => $cat_name)
    {
        // Display the item:
        echo "<li><a href='" . strtolower( "$parents" . "/" . "$cat_name"). "'>$cat_name</a>";

        // Check for children:
        if (isset($categories[$cat_id]))
        {
            // Add parents to URL if applicable
            $new_parents = $parents . '/' . $cat_name;
            
            // Do recursion:
            make_list($categories[$cat_id], $new_parents);
        }

        // Complete the list item:
        echo '</li>';
    }

    // Close the list:
    echo '</ul>';

} // End of make_list() function.


// $categories['parent_id']['category_id'] = 'category_name';
$categories[0][1] = 'Shirts';
$categories[1][2] = 'Mens';
$categories[0][3] = 'Pants';
$categories[3][4] = 'Tight';
$categories[3][5] = 'Loose';
$categories[5][6] = 'With_Belt_Buckles';
$categories[3][7] = 'Baggy';
$categories[5][8] = 'With_Suspenders';
$categories[0][9] = 'Shoes';
$categories[6][10] = 'Green';


// For debugging:
//echo '<pre>' . print_r($categories,1) . '</pre>';

// Send the first array element
// to the make_list() function:
make_list($categories[0]);

?&gt;
Short but sweet!


Digg   Delicious   Reddit   Facebook   Twitter   StumbleUpon  


  Theme © 2014 iAndrew  
Powered By MyBB, © 2002-2021 MyBB Group.