More recursive madness - El Forum - 04-20-2010
[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-cart/src/tip/application/models/product_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.
More recursive madness - El Forum - 04-20-2010
[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)
More recursive madness - El Forum - 04-20-2010
[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.
More recursive madness - El Forum - 04-20-2010
[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-cart/src/85b232acc984/sql/community_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.
More recursive madness - El Forum - 04-21-2010
[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....
More recursive madness - El Forum - 04-21-2010
[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.
More recursive madness - El Forum - 04-21-2010
[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: <?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]);
?>
Short but sweet!
|