I'm trying to implement a category system on my website. The problem is that a category can have child.
So far, my table category looks like that :
id, name, parent_id
So far, I made a loop, but it only works for 2 levels. Here is my code :
for($i=0;$i<count($data);$i++){
$tree[$data[$i]->name] = array();
for($j=0;$j<count($data);$j++){
if($data[$j]->parent_id == $data[$i]->id){
$tree[$data[$i]->name][] = $data[$j]->name;
}
}
}
It return me an array as it :
Array
(
[0] => Array
(
[1] => Cat1
[children] => Array
(
[12] => sub cat 1
[13] => sub cat 2
[14] => sub cat 3
)
)
[1] => Array
(
[2] => Cat2
)
[2] => Array
(
[3] => Cat3
)
)
How can I make it efficient and recursive to have something more like :
Array
(
[0] => Array
(
[1] => Cat1
[children] => Array
(
[12] => sub cat 1
[13] => sub cat 2
[14] => sub cat 3
[children] => Array
(
[1] => sub sub cat 1
)
)
)
[1] => Array
(
[2] => Cat2
)
[2] => Array
(
[3] => Cat3
)
)
Thanks for your help
EDIT
I'm working on Zend, and it return me the data as it :
Zend_Db_Table_Rowset Object
(
[_data:protected] => Array
(
[0] => Array
(
[id] => 1
[name] => Cinema
[type] => category
[slug] => cinema
[parent_id] => -1
)
[1] => Array
(
[id] => 2
[name] => Horror
[type] => category
[slug] => horror
[parent_id] => 1
)
If you want to apply this type of structure best to use child in other table with parent id as a foreign key.
so with that u can have recursive category posting.
so the table structure will be like this
ch_id, parent_id, child
You should check out Nested Sets, a methodology to store and retrieve date trees in/out of relational databases (what in fact your categories are). For this, you have to adapt your database scheme a little bit (as described in the link or in the several tutorials about nested sets). For PHP, there are several libraries providing Nested Set capabilities, for instance this list.
This should help you out.
It Iterates over every member of the array and checks it's position in the tree structure per level and dependency.
function get_children($data, $parent_id = false) {
$result = array();
foreach ($data as $i => $content) {
if ($parent_id != false && $content['id'] != $parent_id) # other levels
continue;
if ($parent_id === false && !empty($content['parent_id'])) # top level
continue;
$leaf = array(
'id' => $content['id'],
'parent_id' => $content['parent_id'],
'name' => $conten['name']
);
$leaf['children'] = get_children($data, $leaf['id']);
$result[$leaf['id']] = $leaf;
}
return $result;
}
$result = get_children($data);
print_r($result);
Edit: bug fixes
Related
Hello I have this response from my sql query :
Array
(
[0] => Array
(
[id] => 1
[name] => Category #1
[parent] => 0
)
[1] => Array
(
[id] => 2
[name] => Category #2
[parent] => 0
)
[2] => Array
(
[id] => 3
[name] => Category #3
[parent] => 0
)
[3] => Array
(
[id] => 4
[name] => Category #4
[parent] => 0
)
[4] => Array
(
[id] => 5
[name] => Sub category of category #1
[parent] => 1
)
[5] => Array
(
[id] => 6
[name] => Sub category of category #2
[parent] => 2
)
[6] => Array
(
[id] => 7
[name] => Sub category of category #6 (which is sub category of category #2)
[parent] => 6
)
)
After converting the response must looks like this :
array
(
array
(
[id] => 1
[name] => Category #1
[parent] => 0
[children]=> array
(
[id] => 5
[name] => Sub category of category #1
[parent] => 1
)
)
array
(
[id] => 2
[name] => За дома
[parent] => 0
[children]=> array
(
[id] => 6
[name] => Sub category of category #2
[parent] => 2
[children] => array
(
[id] => 7
[name] => Sub category of category #6 (which is sub category of category #2)
[parent] => 6
)
)
)
array
(
[id] => 3
[name] => Category #3
[parent] => 0
)
array
(
[id] => 4
[name] => Category #4
[parent] => 0
)
)
My question is - what is the best way to build such a multidimensional array (in php for example).
Try something like this
$arr = 'Your array';
$new = array();
foreach ($arr as $a){
$new[$a['parent']][] = $a;
}
$data = createMyTree($new, array($arr[0]));
print_r($data);
function createMyTree(&$list, $parent){
$tree = array();
foreach ($parent as $k=>$l){
if(isset($list[$l['id']])){
$l['children'] = createMyTree($list, $list[$l['id']]);
}
$tree[] = $l;
}
return $tree;
}
You can just store the data in a regular array and use code to traverse it back. So each node has its parentId in it, and as long as the parentId is not 0 you traverse back another parent till you get to the root parent of a given node.
you really do not need to think of it as a multi-dimensional array; which you will not be able to do any how because of the infinite dimensions. In other languages, such as C, you could just build a tree implementation (a classic data structure) using pointers. But in PHP i would just use a flat array, store the parentId, and use code to recurse.
I have the following array structure, which is generated from a database:
Array
(
[0] => stdClass Object
(
[id] => 1
[parent] => 0
[children] => Array
(
[0] => stdClass Object
(
[id] => 2
[parent] => 1
[children] => Array
(
[0] => stdClass Object
(
[id] => 3
[parent] => 2
)
)
)
[1] => stdClass Object
(
[id] => 7
[parent] => 1
)
)
)
[1] => stdClass Object
(
[id] => 4
[parent] => 0
[children] => Array
(
[0] => stdClass Object
(
[id] => 5
[parent] => 4
[children] => Array
(
[0] => stdClass Object
(
[id] => 6
[parent] => 5
)
)
)
)
)
)
What I'd like to do is present this information in a HTML <select> box, with correct indentation to indicate the structure. So, for the given example, the result should look something like this:
- Select category
- Category 1
- Category 2
- Category 3
- Category 7
- Category 4
- Category 5
- Category 6
I am currently looping over the content using PHP's RecursiveIteratorIterator() class, which works to output all of them, but I can't figure out how to include the indentation. Here's the code I have now:
$html = '<select>';
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($tree), RecursiveIteratorIterator::SELF_FIRST);
foreach($iterator as $i => $cat)
{
if($cat->id != '')
{
$option = new self($this->_db, $cat->id);
$html.= '<option value="'.$cat->id.'"';
$html.= '>'.$option->name;
$html.= '</option>';
}
}
$html.= '</select>';
return $html;
Can anyone point me in the right direction. Essentially, all I want to do is pad the option text by x spaces per nest depth.
If you are just wanting to pad the option name, you could use RecursiveIteratorIterator::getDepth and add the padding multiplied by the current depth:
$option_padding = str_repeat(" ", 4 * $cat->getDepth() );
I have an piece of my array here:
[0] => stdClass Object
(
[id] => 8005
[parentid] => 8004
[name] => a, b
[level] => 1
[prodtypename] => 1, 2, 3, 4
)
[1] => stdClass Object
(
[id] => 8006
[parentid] => 8005
[name] => c
[level] => 2
[prodtypename] =>
)
[2] => stdClass Object
(
[id] => 8007
[parentid] => 8005
[name] => d
[level] => 2
[prodtypename] =>
)
In my form I display them like:
<?php foreach ($this->myArray as $item): ?>
<?php echo $item->prodtypename ?>
it displays me current category and its current type. I need to display parent "prodtypename" next to child type, so we can know, what is parents types for current child category. Im trying to make display look something like this:
[1] => stdClass Object
(
[id] => 8006
[parentid] => 8005
[name] => c
[level] => 2
[prodtypename] => 1,2,3,4
)
Because parents "prodtypename" have 1.2.3.4, so the child inherits it. Thanks.
Based on the array you have already something like this might work assuming that you can rely on parents always appearing in the array before their children:
$prodTypeNames = array();
foreach ($this->myArray as $item) {
$prodTypeName = $item->prodtypename;
if( is_null( $item->prodtypename ) && isset( $prodTypeNames[$item->parentid] ) ) {
$prodTypeName = $prodTypeNames[$item->parentid];
}
$prodTypeNames[$item->id] = $prodTypeName;
echo $prodTypeName;
}
But you're probably better off getting the data in a better format to begin with.
Is there a way to foreach() through one array based on a matching value with a different key in another array? In this example, I have a category array ($cat_data) with cat_id as a key and an image array ($img_data) with category_id as a key.
Array (
[0] => Array (
[cat_id] => 1
[cat_name] => Category 1
)
[1] => Array (
[cat_id] => 2
[cat_name] => Category 2
)
)
Array (
[0] => Array (
[img_id] => 2
[img_name] => demo1.jpg
[img_label] => Demo 1
[category_id] => 2
[img_order] => 1
)
[1] => Array (
[img_id] => 3
[img_name] => demo2.jpg
[img_label] => Demo 2
[category_id] => 2
[img_order] => 2
)
[2] => Array (
[img_id] => 4
[img_name] => demo3.jpg
[img_label] => Demo 3
[category_id] => 1
[img_order] => 1
)
)
What I want is to output my display so it looks like the following:
Category 1
demo3.jpg
Category 2
demo1.jpg
demo2.jpg
Since I'm really not great at fully grasping arrays, I thought I'd try Stack, and I haven't been able to find an answer to my question, partially because I'm not sure what to ask for precisely. Any help??
The naïve way:
foreach ($cat_data as $cat) {
echo $cat['cat_name'];
foreach ($img_data as $img) {
if ($img['category_id'] != $cat['cat_id']) {
continue;
}
echo $img['img_name'];
}
}
This is rather inefficient, since it loops through the $imgs array several times, but easy and works.
More efficient:
$images = array();
foreach ($img_data as $img) {
$images[$img['category_id']][] = $img;
}
foreach ($cat_data as $cat) {
echo $cat['cat_name'];
if (isset($images[$cat['cat_id']])) {
foreach ($images[$cat['cat_id']] as $img) {
echo $img['img_name'];
}
}
}
This first groups all images by category into a new array, which you can then loop over directly once.
I would urge you to redesign your array when you fill them with data to instead look something like this.
Array (
[0] => Array (
[cat_id] => 1
[cat_name] => Category 1
[images] = Array(
[0] => Array (
[img_id] => 4
[img_name] => demo3.jpg
[img_label] => Demo 3
[category_id] => 1
[img_order] => 1
)
)
)
[1] => Array (
[cat_id] => 2
[cat_name] => Category 2
[images] = Array(
[0] => Array (
[img_id] => 4
[img_name] => demo3.jpg
[img_label] => Demo 3
[category_id] => 1
[img_order] => 1
)
[1] => Array (
[img_id] => 2
[img_name] => demo1.jpg
[img_label] => Demo 1
[category_id] => 2
[img_order] => 1
)
)
)
)
Then you would have all the relational data connected and would just have to loop through your array of categories and print the images associated with each one in turn. The numbered indexes could even be changed to associative names if the id of the catagory weren't important for example. Then the array could be indexed with the name of the category and just contain the images of that category.
If the images are to be used in other places where you initial layout of those fits better you could still use this layout for your main data graph. Just replace the actual data of the images in the images array under each category with a reference to the actual image object.
I've been working on a site that uses binary mlm system.
Illustration here
So I have a two tables in database, users anad relationships. There is ID and personal data columns in users. Relationships has 4 columns: ID, parentID, childID, pos. Where pos is either left or right.
I have succesfully written a function that recursively lists all children of given pid (parentID). However I need to sort it in levels (for display and calculation purposes).
I have an array of children of user ID = 1:
Array
(
[0] => Array
(
[id] => 2
[parentID] => 1
[pos] => l
)
[1] => Array
(
[id] => 4
[parentID] => 2
[pos] => l
)
[2] => Array
(
[id] => 8
[parentID] => 4
[pos] => l
)
[3] => Array
(
[id] => 5
[parentID] => 2
[pos] => p
)
[4] => Array
(
[id] => 3
[parentID] => 1
[pos] => p
)
[5] => Array
(
[id] => 6
[parentID] => 3
[pos] => l
)
[6] => Array
(
[id] => 7
[parentID] => 3
[pos] => p
)
)
Now I have function named get_levels that returns an multidimensional array that should look like this:
Array
(
[0] => Array
(
[0] => Array
(
[id] => 2
[parentID] => 1
[pos] => l
)
[1] => Array
(
[id] => 3
[parentID] => 1
[pos] => p
)
)
[1] => Array
(
[0] => Array
(
[id] => 4
[parentID] => 2
[pos] => l
)
[1] => Array
(
[id] => 5
[parentID] => 2
[pos] => p
)
[2] => Array
(
[id] => 6
[parentID] => 3
[pos] => l
)
[3] => Array
(
[id] => 7
[parentID] => 3
[pos] => p
)
)
ETC.
)
Here's the function:
function get_levels($pid,$level, $level_id){
$children = children_array($pid,1);
if (sizeof($children) > 0):
foreach ($children as $child):
if ($child["parentID"] == $pid):
get_levels($child["id"], $level, $level_id+1);
$level[$level_id][] = $child;
endif;
endforeach;
endif;
return $level;
}
function children_array($pid, $depth) returns the children ... for $depth = 1 it returns immediate children (0 or 1 or 2), for $depth = 0 it returns all children
Can anyone help me with this function? I think the function works, however I don't know how to recursively use and add to array.
Looks like you're using a data structure within the wrong context. It's a binary tree, yet it's represented into a multilevel array which in short doesn't define its boundaries and rules of use.
When using the tree, I would use something like a Node class that has two children, left and right. Iterating through the tree would be piece of cake, inserting/deleting/editing into it is easily done depending on which set of rules you want to follow. When storing the tree, I would use some kind of Ahnentafel list which can easily be done in a relational database.
I would in no way mix both iteration and storage processes because if I change the rules of storage, I might also have to change the rules of iteration and vice versa.