we have this array from mysqli query output :
$items = Array
(
Array
(
'id' => 1,
'title' => 'menu1',
'parent_id' => 0
),
Array
(
'id' => 2,
'title' => 'submenu1-1',
'parent_id' => 1
),
Array
(
'id' => 3,
'title' => 'submenu1-2',
'parent_id' => 1
),
Array
(
'id' => 4,
'title' => 'menu2',
'parent_id' => 0
),
Array
(
'id' => 5,
'title' => 'submenu2-1',
'parent_id' => 4
)
);
and we need this html output with php :
<ul>
<li><a>menu1</a>
<ul>
<li><a>submenu1-1</a></li>
<li><a>submenu1-2</a></li>
</ul>
</li>
<li><a>menu2</a>
<ul>
<li><a>submenu2-1</a></li>
</ul>
</li>
</ul>
can anyone help me ?
Probably this is very easy but I have tried everything already without success !!
finally i found answer like this:
function generateTreeMenu($datas, $parent = 0, $limit=0){
if($limit > 1000) return '';
$tree = '';
$tree = '<ul>';
for($i=0, $ni=count($datas); $i < $ni; $i++){
if($datas[$i]['parent_id'] == $parent){
$tree .= '<li><a>';
$tree .= $datas[$i]['title'].'</a>';
$tree .= generatePageTree($datas, $datas[$i]['id'], $limit++);
$tree .= '</li>';
}
}
$tree .= '</ul>';
return $tree;
}
echo generateTreeMenu($items);
//index elements by id
foreach ($items as $item) {
$item['subs'] = array();
$indexedItems[$item['id']] = (object) $item;
}
//assign to parent
$topLevel = array();
foreach ($indexedItems as $item) {
if ($item->parent_id == 0) {
$topLevel[] = $item;
} else {
$indexedItems[$item->parent_id]->subs[] = $item;
}
}
//recursive function
function renderMenu($items) {
$render = '<ul>';
foreach ($items as $item) {
$render .= '<li>' . $item->title;
if (!empty($item->subs)) {
$render .= renderMenu($item->subs);
}
$render .= '</li>';
}
return $render . '</ul>';
}
echo renderMenu($topLevel);
The problem here is just the structure of the array, so first you can convert the array to a more suitable structure, then you can draw your list easily.
Here is a function to convert the array:
function makeTree( $rst, $level, &$tree )
{
for ( $i=0, $n=count($rst); $i < $n; $i++ )
{
if ( $rst[$i]['parent_id'] == $level )
{
$branch = array(
'id' => $rst[$i]['id'],
'title' => $rst[$i]['title'],
'children' => array()
);
makeTree( $rst, $rst[$i]['id'], $branch['children'] );
$tree[] = $branch;
}
}
}
Mode of use:
$tree = array();
makeTree( $originalArray, 0, $tree );
At the end, you will have a new array in $tree structured as shown below, which you can easily draw in your view.
Array
(
[0] => Array
(
[id] => 1
[title] => menu1
[children] => Array
(
[0] => Array
(
[id] => 2
[title] => submenu1-1
[children] => Array
(
)
)
[1] => Array
(
[id] => 3
[title] => submenu1-2
[children] => Array
(
)
)
)
)
[1] => Array
(
[id] => 4
[title] => menu2
[children] => Array
(
[0] => Array
(
[id] => 5
[title] => submenu2-1
[children] => Array
(
)
)
)
)
)
Try this
$node = array();
foreach ($items as $item) {
if ($item['parent_id'] == 0) {
$node[$item['id']][$item['id']] = $item['title'];
} else {
$node[$item['parent_id']][$item['id']] = $item['title'];
}
}
$result = array();
foreach ($node as $key => $value) {
$result[$value[$key]] = array_diff($value, array($key => $value[$key]));
}
Related
Im trying to create nested blocks of HTML code from a Multidimensional Array in PHP using a recursive function. However I can not seem to print the tags in a nested order.
Here is the original array:
$array_1 =
array
(
array
(
'id' => '1',
'tag' => 'div1',
'parent' => '0'
),
array
(
'id' => '2',
'tag' => 'div2',
'parent' => '1'
),
array
(
'id' => '3',
'tag' => 'div3',
'parent' => '2'
),
array
(
'id' => '4',
'tag' => 'div4',
'parent' => '2'
),
array
(
'id' => '5',
'tag' => 'div5',
'parent' => '0'
),
array
(
'id' => '6',
'tag' => 'div6',
'parent' => '5'
),
array
(
'id' => '7',
'tag' => 'div7',
'parent' => '0'
)
);
The first thing I do is to use a function to turn this array into a multidimensional array by building a tree structure using the parent element of each record as reference:
function buildTree(array $elements, $parentId = 0) {
$branch = array();
foreach ($elements as $element)
{
if ($element['parent'] == $parentId)
{
$children = buildTree($elements, $element['id']);
if ($children)
{
$element['children'] = $children;
}
$branch[] = $element;
}
}
return $branch;
}
$tree_1 = buildTree($array_1);
Once this is done the multidimensional array should look like this:
Array
(
[0] => Array
(
[id] => 1
[name] => div1
[parent] => 0
[children] => Array
(
[0] => Array
(
[id] => 2
[name] => div2
[parent] => 1
[children] => Array
(
[0] => Array
(
[id] => 3
[name] => div3
[parent] => 2
)
[1] => Array
(
[id] => 4
[name] => div4
[parent] => 2
)
)
)
)
)
[1] => Array
(
[id] => 5
[name] => div5
[parent] => 0
[children] => Array
(
[0] => Array
(
[id] => 6
[name] => div6
[parent] => 5
)
)
)
[2] => Array
(
[id] => 7
[name] => div7
[parent] => 0
)
)
The next thing to do is to output the html elements in a nested order. In this example Im using symbolic tag names such as div1, div2, etc; but in reality they could be any html tags like divs, h1, ul,li, form, etc; with their opening and closing tags.
To accomplish this I use the following recursive function, however my problem is that it does not show the html elements in a nested order as they should be.
function recursive($array, $level = 0)
{
foreach($array as $key => $value)
{
if (!is_array($value) && $key == "tag") { echo str_repeat(" ", $level), "[".$value."]", ''; }
//If $value is an array.
if(is_array($value))
{
//We need to loop through it.
recursive($value, $level + 1);
}
else
{
//It is not an array, so print it out.
if ($key == "tag") { echo "[/".$value."]", '<br>'; }
}
}
}
$tree_2 = recursive($tree_1);
echo $tree_2;
This is how it currently outputs the html tags from the array:
[div1][/div1]
[div2][/div2]
[div3][/div3]
[div4][/div4]
[div5][/div5]
[div6][/div6]
[div7][/div7]
And this is how the html tags should be output in reality based on the multidimensional array's nested order (please note how some div tags contain other div tags before their closing element):
[div1]
[div2]
[div3][/div3]
[div4][/div4]
[/div2]
[/div1]
[div5]
[div6][/div6]
[/div5]
[div7][/div7]
How can I get the html tags printed in the right nested order like in the last example? What am I missing? Thanks so much!
The issue is that the key tag is before children in the $tree_1 array.
Whilst I would personally change your data structure around to make it "easier" to use in the recursive function, you can change your recursive function to make it work like this:
function recursive($array, $level = 0)
{
// Check if we are using an associative array or indexed array
if(isset($array['tag'])) {
echo str_repeat(" ", $level), "[" . $array['tag'] . "]", '';
if(isset($array['children'])) {
echo "<br />";
recursive($array['children'], $level + 1);
echo str_repeat(" ", $level), "[/" . $array['tag'] . "]" , '';
} else {
echo "[/" . $array['tag'] . "]";
}
echo "<br />";
} else {
foreach($array as $key => $value)
{
recursive($value, $level + 1);
}
}
}
Add additional line breaks and formatting where required
A rewrite of your function:
function recursive2($array, &$level, $indentClosingTag = true)
{
$level++;
for($i = 0; $i<count($array); $i++) {
$data = $array[$i];
$indent = str_repeat(' ', $level);
echo $indent . "[".$data['tag'].']';
if (isset($data['children'])) {
echo '<br>';
$level++;
recursive2($data['children'], $level, true);
$level--;
} else {
$indentClosingTag = false;
}
echo ($indentClosingTag ? $indent : ''). "[/".$data['tag'].']'.'<br>';
}
$level--;
}
$l = 0;
recursive2($tree_1, $l);
I'm using a foreach loop inside my recursive function. But I have trouble figuring out where to pass my return statement. I need to return my temp array at some point, but I'm not sure how to do this:
<?php
$patterns = function($array, $temp = array(), $i = 0, $id = 0, $parent = 0) use(&$patterns) {
$return = null;
if(array_key_exists($i, $array)) {
foreach($array[$i] as $set) {
if($parent == $set['id']) {
$data = array(
'id' => $set['id'],
'parent' => $set['parent']
);
array_push($temp, $data);
}
$patterns($array, $temp, $i + 1, $set['id'], $set['parent']);
}
}
};
print_r($patterns($rev_relations));
?>
This is my data:
Array(
[0] => Array(
[0] => Array(
[id] => 60
[parent] => 55
)
[1] => Array(
[id] => 57
[parent] => 54
)
)
[1] => Array(
[0] => Array(
[id] => 61
[parent] => 50
)
[1] => Array(
[id] => 54
[parent] => 49
)
)
[2] => Array(
[0] => Array(
[id] => 49
[parent] => 0
)
)
)
<?php
//pass $temp by reference so outside variable gets populated
$patterns = function($array, &$temp, $i = 0, $id = 0, $parent = 0) use(&$patterns) {
$return = null;
if(array_key_exists($i, $array)) {
foreach($array[$i] as $set) {
if($parent == $set['id']) {
$data = array(
'id' => $set['id'],
'parent' => $set['parent']
);
array_push($temp, $data);
}
$patterns($array, $temp, $i + 1, $set['id'], $set['parent']);
}
}
};
//actuall array is created on temp here
$temp=array();
$patterns($rev_relations,$temp);
?>
wont this work? Never really worked with nameless functions, but this is how i would go about it on a normall recursive function
Ok, take two
<?php
$patterns = function($array, $temp = array(), $i = 0, $id = 0, $parent = 0) use(&$patterns) {
$return = null;
if(array_key_exists($i, $array)) {
foreach($array[$i] as $set) {
if($parent == $set['id']) {
$data = array(
'id' => $set['id'],
'parent' => $set['parent']
);
array_push($temp, $data);
}
return $patterns($array, $temp, $i + 1, $set['id'], $set['parent']);
}
}
else
{
return $temp;
}
};
$patterns($rev_relations);
?>
Here is what i have got http://codepad.org/iDoXXsLX
Have array like this
Array
(
[0] => Array
(
[NumberRenamed] => 17
[TopicName] => Products
[UpperLevelNumberRenamed] => 0
)
[17] => Array
(
[0] => Array
(
[1] => Array
(
[NumberRenamed] => 18
[TopicName] => Computers
[UpperLevelNumberRenamed] => 17
)
)
)
[18] => Array
(
[0] => Array
(
[2] => Array
(
[NumberRenamed] => 16
[TopicName] => Laptops
[UpperLevelNumberRenamed] => 18
)
)
)
[16] => Array
(
[0] => Array
(
[4] => Array
(
[NumberRenamed] => 8
[TopicName] => Dell
[UpperLevelNumberRenamed] => 16
)
)
)
)
Top level item is Products, first sub-level item is Computers, next sub-level is Laptops, then again next sub-level Dell
For each sub-level item UpperLevelNumberRenamed == to closest upper level NumberRenamed.
Want to get result like this
Products
Computers
Laptops
Dell
Acer
Desktops
Home
Tried this
foreach( $main_topics as $k_main_topics => $v_main_topics ){
if( isset($v_main_topics['UpperLevelNumberRenamed']) and $v_main_topics['UpperLevelNumberRenamed'] == 0 ){
//print only top level topics
echo $v_main_topics['TopicName']. '<br/>';
}
else{//if not top level topic
foreach( $v_main_topics[0] as $k_v_main_topics_0 => $v_v_main_topics_0 ){
echo $v_v_main_topics_0['TopicName']. '<br/>';
}//foreach( $v_main_topics[0] as $k_v_main_topics_0 => $v_v_main_topics_0 )
}//else{
}//foreach( $main_topics as $k_main_topics => $v_main_topics )
But get this
Products
Home
Computers
Laptops
Desktops
Dell
Acer
Something incorrect, but can not understand what. Please, advice what need to correct/change in the code
Trying another way
Initial array is one dimensional array. Trying to get ul li navigation from one dimensional.
Here is what i did http://codepad.org/OLtxyL4X
Here's a summary of what it does:
flatten the array recursively
build a multi-dimensional relation map
create 1D relationships that link UpperLevelNumberRenamed to NumberRenamed
print out the multi-dimensional as an ul-li list.
Here it is:
$flat = array();
foreach (new RecursiveIteratorIterator(new RecursiveArrayIterator($main_topics)) as $i)
$flat[] = $i;
$final = array();
$defs = array();
for ($i = 0; $i < count($flat); $i += 3)
if ($flat[$i + 2] == 0) {
$final[$flat[$i + 1]] = array();
$defs[$flat[$i]] = &$final[$flat[$i + 1]];
} else {
$defs[$flat[$i + 2]][$flat[$i + 1]] = array();
$defs[$flat[$i]] = &$defs[$flat[$i + 2]][$flat[$i + 1]];
}
function array2ul($array) {
$out = "<ul>";
foreach($array as $key => $elem)
$out = is_array($elem) ?
$out . "<li><span>$key</span>" . array2ul($elem) . "</li>" :
$out = $out."<li><span>$key:[$elem]</span></li>";
$out = $out . "</ul>";
return $out;
}
echo array2ul($final);
Output:
<ul><li><span>Products</span><ul><li><span>Computers</span><ul><li><span>Laptops</span><ul><li><span>Dell</span><ul></ul></li><li><span>Acer</span><ul></ul></li></ul></li><li><span>Desktops</span><ul></ul></li></ul></li></ul></li><li><span>Home</span><ul></ul></li></ul>
This shall be a working example using recursion, not tested though:
Define the array
$main_array = Array
(
'10' => Array
(
'name' => 'Products'
'children' => Array
(
'12' => Array
(
'name' => 'Laptop',
'children' => Array
(
'13' => Array
(
'name' => 'Dell',
),
'14' => Array
(
'name' => 'Acer',
)
)
)
'14' => Array
(
'name' => 'Desktop',
'children' => Array
(
'15' => Array
(
'name' => 'Sony',
),
'16' => Array
(
'name' => 'Apple',
)
)
),
)
)
)
Create and call the function :
function createList($main_topics)
{
if($main_topics == null || sizeof($main_topics) <= 0)
{
return '';
}
$list = '<ul>';
foreach($main_topics as $k_main_topics => $v_main_topics )
{
$list .= '<li id="' . $k_main_topics'"> '. $v_main_topics['name'] . ' ' . createList(isset($v_main_topics["children"]) ? $v_main_topics["children"] : null) . '</li>' ;
}
$list .= '</ul>';
return $list;
}
echo createList($main_array);
I have an array storing all subcategories of root category, for example:
I just want to list them like this:
A
A>B
A>B>C
A>B>C>E
A>B>D
A>B>D>F
A>B>G
A>B>G>H
My array:
Array (
[0] => Array (
[id] => 2
[title] => B
[sub_category] => Array (
[0] => Array (
[id] => 3
[title] => C
[sub_category] => Array (
[0] => Array (
[id] => 5
[title] => E
[sub_category] => Array ()
)
)
)
[1] => Array (
[id] => 4
[title] => D
[sub_category] => Array (
[0] => Array (
[id] => 6
[title] => F
[sub_category] => Array ()
)
)
)
[2] => Array (
[id] => 7
[title] => G
[sub_category] => Array (
[0] => Array (
[id] => 10
[title] => H
[sub_category] => Array ()
)
)
)
)
)
)
My functions are listing them with no '>' and with no parents, just title.
Here are my functions:
function buildTree($tree) {
foreach ($tree as $node)
{
echo '<li>'.$node['title'].'</li>';
if (!empty($node['sub_category'])) {
echo '<ul>';
buildTree($node['sub_category']);
echo '</ul>';
}
}
}
function categoryTree($db, $root_id) {
$tree = array();
$sub_categories = $db->get_results('SELECT * FROM categories WHERE parentid="'.$root_id.'"');
if ($sub_categories) {
foreach ($sub_categories as $sub_category) {
$tree[] = array(
"id" => $sub_category->id,
"title" => $sub_category->title,
"sub_category" => categoryTree($db, $sub_category->id)
);
}
}
return $tree;
}
It's solved. Here is my new code:
function buildTree($db, $tree) {
foreach ($tree as $node)
{
$header = array();
$id = $node['id'];
$can_i_stop = false;
$has_parent = $db->get_var('SELECT parentid FROM categories WHERE id="'.$node['id'].'"');
if (!($has_parent == '') && !($has_parent == null)) {
$seperator = ' > ';
}else{
$seperator = '';
}
while ($can_i_stop == false) {
$parent_id = $db->get_var('SELECT parentid FROM categories WHERE id="'.$id.'"');
if (!($parent_id == '') && !($parent_id == null)) {
$parent_title = $db->get_var('SELECT title FROM categories WHERE id="'.$parent_id.'"');
array_push($header, $parent_title);
$id = $parent_id;
$can_i_stop = false;
}else{
$can_i_stop = true;
}
}
echo '<li>'.implode(' > ', array_reverse($header)).$seperator.$node['title'].'</li>';
if (!empty($node['sub_category'])) {
echo '<ul>';
buildTree($db, $node['sub_category']);
echo '</ul>';
}
}
}
function categorySubTree($db, $root_id) {
$tree = array();
$sub_categories = $db->get_results('SELECT * FROM categories WHERE parentid="'.$root_id.'"');
if ($sub_categories) {
foreach ($sub_categories as $sub_category) {
$tree[] = array(
"id" => $sub_category->id,
"title" => $sub_category->title,
"sub_category" => categorySubTree($db, $sub_category->id)
);
}
}
return $tree;
}
function categoriesTree($db) {
$tree = array();
$categories = $db->get_results('SELECT * FROM categories WHERE is_subcategory=0');
if ($categories) {
foreach ($categories as $category) {
$tree[] = array(
"id" => $category->id,
"title" => $category->title,
"sub_category" => categorySubTree($db, $category->id)
);
}
}
return $tree;
}
How can I turn the first array in to the second one? The goal is to create an array that shows the hierarchy, based on location_id and parent_id. Each location_name should be in an array of which the key is its parent_id.
Ignore the values I gave to location_name. No value for parent_id == NULL, these are the top level items.
First array:
Array
(
[0] => stdClass Object
(
[location_id] => 1
[location_name] => Town 1
[parent_id] =>
)
[1] => stdClass Object
(
[location_id] => 2
[location_name] => Town 1.1
[parent_id] =>
)
[2] => stdClass Object
(
[location_id] => 3
[location_name] => Town 1.2
[parent_id] => 1
)
[3] => stdClass Object
(
[location_id] => 4
[location_name] => Town 1.3
[parent_id] => 1
)
[4] => stdClass Object
(
[location_id] => 5
[location_name] => town 1.1.1
[parent_id] => 2
)
[5] => stdClass Object
(
[location_id] => 6
[location_name] => Town 1.1.2
[parent_id] => 3
)
);
Resulting array should be:
Array(
'Town 1' = array(
'town 1.2',
'town 1.3' = array(
'town 1.1.2'
)
),
'Town 2' = array(
'town 1.1.1'
)
);
EDIT: working solution based on Rijk's answer
function _order_locs($parent, $array)
{
$return = array();
foreach ( $array as $town )
{
if ( $town->parent_id == $parent )
{
$set = $this->_order_locs( $town->location_id, $array );
if( $this->_menu_is_parent($town->location_id, $array) ) $return[$town->location_name] = $set;
else $return[] = $town->location_name;
}
}
return $return;
}
function _menu_is_parent($id, $array)
{
foreach( $array as $a )
{
if( $a->parent_id == $id ) return TRUE;
}
}
You have to loop through it, using a recursive function (one that calls itself):
function getChilds( $parent, $array ) {
$return = array();
foreach ( $array as $town ) {
if ( $town['location_id'] == $parent ) {
$return[] = array(
'name' => $town['location_name'],
'childs' => getChilds( $town['location_id'], $array )
);
}
}
return $return;
}
$towns_tree = getChilds( 0, $towns );
Might not work right off the bat, but that gives you a nice oppurtunity to play with the code and get familiar with this concept ;)
Here is some code that will more or less do what you need. You will have to tweak it to your liking.
<?php
Class Node {
public $id;
public $parent_id;
public $value;
public $children;
public $depth;
function __construct($id, $parent_id, $value) {
$this->id = $id;
$this->parent_id = $parent_id;
$this->value = $value;
$this->children = array();
$this->depth = 0;
}
function add_child(Node $new_child) {
if ($new_child->parent_id == $this->id) {
$this->children[$new_child->id] = $new_child;
$this->children[$new_child->id]->depth = $this->depth + 1;
} else {
foreach ($this->children as $child) {
$child->add_child($new_child);
}
}
}
function to_array() {
if (count($this->children) > 0) {
$arr = array();
foreach ($this->children as $child) {
array_push($arr, $child->to_array());
}
return array($this->value => $arr);
} else {
return $this->value;
}
}
function str() {
echo str_repeat(" ", $this->depth) . $this->value . "\n";
foreach ($this->children as $child) {
$child->str();
}
}
}
?>
Here is some sample code to test it with:
<?php
$arr = Array(
array('location_id' => 1,
'location_name' => 'Town 1',
'parent_id' => 0),
array('location_id' => 2,
'location_name' => 'Town 1.1',
'parent_id' => 0),
array('location_id' => 3,
'location_name' => 'Town 1.2',
'parent_id' => 1),
array('location_id' => 4,
'location_name' => 'Town 1.3',
'parent_id' => 1),
array('location_id' => 5,
'location_name' => 'Town 1.1.1',
'parent_id' => 2),
array('location_id' => 6,
'location_name' => 'Town 1.1.2',
'parent_id' => 3)
);
$root = new Node(0, 0, 'root');
foreach ($arr as $item) {
$node = new Node($item['location_id'],
$item['parent_id'],
$item['location_name']);
$root->add_child($node);
}
$tree = $root->to_array();
$tree = $tree['root'];
var_dump($tree);
?>