PHP Function That Creates a Nested Ul Li

PHP function that creates a nested ul li?

This would be a excellent example of the use of recursion. An array (with sub-arrays within it) defines each level, and a function loops, calling itself whenever it finds a new array to process. As long as the function cleans up appropriately (closing the </li> & </ol>), it's largely automatic.

<?php
// I know which function I'd rather write....
$tree = array('Projects (Menu Level 1)',
array('Project 1 (Menu Level 2)',
'Project 2 (Menu Level 2)',
'Project 3 (Menu Level 2)'),
'News (Menu Level 1)',
'Contact (Menu Level 1)');

// now quake beneath the power of this fully operational recursive function!
function olLiTree($tree)
{
echo '<ul>';
foreach($tree as $item) {
if (is_array($item)) {
olLiTree($item);
} else {
echo '<li>', $item, '</li>';
}
}
echo '</ul>';
}
olLiTree($tree); // kick off from the top level

php function that create nested ul li from array+object

This should get you started:

It uses a recursive function; it calls itself.

UPDATED

Tested with the JSON object and fixed some errors

<?php    

echo make_ulli($object);

function make_ulli($array){
if(!is_array($array)) return '';

$output = '<ul>';
foreach($array as $item){

$output .= '<li>' . $item->t_name;

if(property_exists($item, 'childs'))
$output .= make_ulli($item->childs);

$output .= '</li>';

}
$output .= '</ul>';

return $output;
}

?>

Creating a nested UL from flat array in PHP

I would gather the data and construct a tree first, and then print the tree out. Some sample code:

foreach ($nodes as $item) {
// gather the data in an assoc array indexed by id
if ( !isset($data_by_id[ $item['top_id'] ])) {
$data_by_id[ $item['top_id'] ] = array( 'name' => $item['standard'] );
}
if ( !isset($data_by_id[ $item['parent_id'] ])) {
$data_by_id[ $item['parent_id'] ] = array( 'name' => $item['indicator'] );
}
if ( !isset($data_by_id[ $item['child_id'] ])) {
$data_by_id[ $item['child_id'] ] = array(
'name' => $item['element'],
'contents' => array(
$item['Connections'],
$item['Effective Practice'],
$item['Proficient'],
$item['Suggested Artifacts'])
);
}
// construct the tree - I've made a simple three tier array
$tree[ $item['top_id'] ][ $item['parent_id'] ][ $item['child_id'] ]++;
}

// go through the tree and print the info
// this is a recursive function that can be used on arbitrarily deep trees
function print_tree( $data, $arr ){
echo "<ul>\n";
// the tree is an associative array where $key is the ID of the node,
// and $value is either an array of child nodes or an integer.
foreach ($arr as $key => $value) {
echo "<li>" . $data[$key]['name'] . "</li>\n";
if (isset($data[$key]['contents']) && is_array($data[$key]['contents'])) {
echo '<ul>';
foreach ($data[$key]['contents'] as $leaf) {
echo '<li>' . $leaf . "</li>\n";
}
echo "</ul>\n";
}
// if $value is an array, it is a set of child nodes--i.e. another tree.
// we can pass that tree to our print_tree function.
if (is_array($value)) {
print_tree($data, $value);
}
}
echo "</ul>\n";
}

print_tree($data_by_id, $tree);

You'll need to add in error checking, zapping of strange characters, removal of excess whitespace, etc.

From php multidimensional array trying to create nested ul li menu (unlimited nested levels)

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>

PHP function generate UL LI

function olLiTree($tree) {
$out = '<ul>';

foreach($tree as $key => $value) {
$out.= '<li>';

if (is_array($value)) {
$out.= $key . olLiTree($value);
} else {
$out.= $value;
}

$out.= '</li>';
}

$out.= '</ul>';

return $out;
}

Always return the final output, don't echo it directly. You'll thank yourself one day when you find a situation where you don't want to echo it immediately. :)

You might want to change the array structure to this, as it's less redundant (works with above code):

$tree = array('A', 'B', 'C' => array('CA', 'CB'), 'D');

php, nested ul/li list from list of strings

You can easily handle this using function and recursion, see example below:

$items = array ('page 1', 'page 2', 'page 3', 'page 4');
recursive($items);
function recursive($items)
{
if (is_array($items) && !empty($items)) {
echo "<ul><li>";
foreach ($items as $item) {
echo array_shift($items);
recursive($items);
break;
}
echo "</li></ul>";
}
}

Demo: https://eval.in/550199

Create nested array from unordered lists in files

Edit: OK, this was not so easy after all :)

By the way, this library is really an excellent tool. Kudos to the guys who wrote it.

Here is one possible solution:

class menu_parse {

static $missing = array(); // list of missing files

static private $files = array(); // list of source files to process

// initiate menu parsing
static function start ($file)
{
// start with root file
self::$files[$file] = 1;

// parse all source files
for ($res=array(); current(self::$files); next(self::$files))
{
// get next file name
$file = key(self::$files);

// parse the file
if (!file_exists ($file))
{
self::$missing[$file] = 1;
continue;
}
$html = file_get_html ($file);

// get menu root (if any)
$root = $html->find("ul[id=menu]",0);
if ($root) self::menu ($root, $res);
}

// reorder missing files array
self::$missing = array_keys (self::$missing);

// that's all folks
return $res;
}

// parse a menu at a given level
static private function menu ($menu, &$res)
{
foreach ($menu->children as $elem)
{
switch ($elem->tag)
{
case "li" : // name and possibly source file of a menu

// grab menu name
$name = $elem->plaintext;

// see if we can find a link to the menu file
$link = $elem->children(0);
if ($link && $link->tag == 'a')
{
// found the link
$file = $link->href;
$res[$name]->file = $file;

// add the source file to the processing list
self::$files[$file] = 1;
}
break;

case "ul" : // go down one level to grab items of the current menu
self::menu ($elem, $res[$name]->childs);
}
}
}
}

Usage:

// The result will be an array of menus indexed by item names.
//
// Each menu will be an object with 2 members
// - file -> source file of the menu
// - childs -> array of menu subtitems
//
$res = menu_parse::start ("root.html");

// parse_menu::$missing will contain all the missing files names

echo "Result : <pre>";
print_r ($res);
echo "</pre><br>missing files:<pre>";
print_r (menu_parse::$missing);
echo "</pre>";

Ouput of your test case:

Array
(
[Start] => stdClass Object
(
[childs] => Array
(
[Sub1] => stdClass Object
(
[file] => file1.html
[childs] => Array
(
[SubSub1] => stdClass Object
(
[file] => file3.html
[childs] => Array
(
[SubSubSub1] => stdClass Object
(
[file] => file7.html
)
[SubSubSub2] => stdClass Object
(
[file] => file8.html
)
[SubSubSub3] => stdClass Object
(
[file] => file9.html
)
)
)
[SubSub2] => stdClass Object
(
[file] => file3.html
)
[SubSub3] => stdClass Object
(
[file] => file5.html
)
[SubSub4] => stdClass Object
(
[file] => file6.html
)
)
)
[Sub2] => stdClass Object
(
[file] => file2.html
)
)
[file] => root.html
)
)

missing files: Array
(
[0] => file2.html
[1] => file5.html
[2] => file6.html
[3] => file7.html
[4] => file8.html
[5] => file9.html
)

Notes:

  • The code assumes all item names are unique inside a given menu.

You could modify the code to have the (sub)menus as an array with numeric indexes and names as properties (so that two items with the same name would not overwrite each other), but that would complicate the structure of the result.

Should such name duplication occur, the best solution would be to rename one of the items, IMHO.

  • The code also assume there is only one root menu.

It could be modified to handle more than one, but that does not make much sense IMHO (it would mean a root menu ID duplication, which would likely cause trouble to the JavaScript trying to process it in the first place).

PHP - Simple Nested Unordered List (UL) Array

This should do the trick:

$result = mysql_query("SELECT * FROM News");
$topicname = '';

// open list of topics
echo '<ul>';

// loop through topics
while($row = mysql_fetch_array($result)) {
if (!$row['TopicID']) {
// fake topic name for unsorted stuff
$row['TopicName'] = 'Sort Me';
}
if ($topicname != $row['TopicName']) {
if($topicname != ''){
// had a topic name, means we opened a list
// that hasn't been closed, close it.
echo '</ul>';
}
// print this topic and open the list of articles
echo '<li>' . $row['TopicName'] . '</li><ul>';
// update the current topic to be this TopicName
$topicname = $row['TopicName'];
}
// the news item
echo '<li>' . $row['NewsID'] . '"</li>';
}
if($topicname != ''){
// we saw at least one TopicName, we need to close
// the last open list.
echo '</ul>';
}
// end topic list
echo '</ul>';

I think your real problem is that you were opening two lists each time, but only closing one (even moving the last block inside the list).


For the second part of your (new) question:

I'll caveat that for larger lists (say, more than 300 items) the tradeoff I'm making with respect to storing the list in memory and iterating twice rather than just querying for counts needed would swing the other way. That is, the solution below puts everything into memory, then iterates a second time to print it out; an alternative would be to run two queries, one to find the number of unique TopicNames and one to find the number of total items in the list.

Also, for display, you really want to solve some optimization for layout, I'll do this naively and just make a (roughly) equal number of topics per column and when the division doesn't work out this will weight towards the left. You'll see where you can tweak or replace some code to get different (and better?) results.

$columns = // user specified;

$result = mysql_query("SELECT * FROM News");
$num_articles = 0;

// $dataset will contain array( 'Topic1' => array('News 1', 'News2'), ... )
$dataset = array();
while($row = mysql_fetch_array($result)) {
if (!$row['TopicID']) {
$row['TopicName'] = 'Sort Me';
}
$dataset[$row['TopicName']][] = $row['NewsID'];
$num_articles++;
}

$num_topics = count($dataset);

// naive topics to column allocation
$topics_per_column = ceil($num_topics / $columns);

$i = 0; // keeps track of number of topics printed
$c = 1; // keeps track of columns printed
foreach($dataset as $topic => $items){
if($i % $topics_per_columnn == 0){
if($i > 0){
echo '</ul></div>';
}
echo '<div class="Columns' . $columns . 'Group' . $c . '"><ul>';
$c++;
}
echo '<li>' . $topic . '</li>';
// this lists the articles under this topic
echo '<ul>';
foreach($items as $article){
echo '<li>' . $article . '</li>';
}
echo '</ul>';
$i++;
}
if($i > 0){
// saw at least one topic, need to close the list.
echo '</ul></div>';
}


Related Topics



Leave a reply



Submit