Solved

PHP - Create UL Nested Lists From Flatfile

Posted on 2013-05-27
15
343 Views
Last Modified: 2013-05-28
(Before anyone asks... I cannot use a MySQL database for this project.)

Using PHP, I need to create nested UL's from a flatfile database. The issue that I'm having is that I don't want duplicate items displayed. I could explain further, but if you'll take a look at the code below, you'll see the data and the goal. Thanks.

FLATFILE DATA:
section|category|service
Section One|Category One|
Section One|Category Two|SC1
Section One|Category Two|SC2
Section One|Category Two|SC3
Section One|Category Two|SC4
Section One|Category Three|
Section Two|Category Four|SC5
Section Two|Category Four|SC6
Section Three|Category Five|SC7

HTML GOAL OUTPUT:
<ul class="section">
	<li>Section One
		<ul class="category">
			<li>Category One</li>
				<!-- no service -->
		</ul> <!-- /category -->
		<ul class="category">
			<li>Category Two</li>
				<ul class="service">
					<li>SC1</li>
					<li>SC2</li>
					<li>SC3</li>
					<li>SC4</li>
				</ul> <!-- /service -->
			</li>
		</ul> <!-- /category -->
		<ul class="category">
			<li>Category Three</li>
				<!-- no service -->
		</ul> <!-- /category -->
	</li>
</ul> <!-- /section -->

<ul class="section">
	<li>Section Two
		<ul class="category">
			<li>Category Four</li>
				<ul class="service">
					<li>SC5</li>
					<li>SC6</li>
				</ul> <!-- /service -->
			</li>
		</ul> <!-- /category -->
	</li>
</ul> <!-- /section -->

<ul class="section">
	<li>Section Three
		<ul class="category">
			<li>Category Five</li>
				<ul class="service">
					<li>SC7</li>
				</ul> <!-- /service -->
			</li>
		</ul> <!-- /category -->
	</li>
</ul> <!-- /section -->

Open in new window

0
Comment
Question by:mar2195
  • 8
  • 5
  • 2
15 Comments
 
LVL 108

Expert Comment

by:Ray Paseur
ID: 39199554
What is the issue?  Have you tried to write any of the PHP code yet?  If so, please show us your start, thanks. ~Ray
0
 
LVL 51

Expert Comment

by:Julian Hansen
ID: 39199633
Look at fgetcsv to read the lines into an array.

Then use a standard catchup processing algorithm to run through the array

i.e. maintain a category and section variable set to the current section and category.

For each item in the array check to see if the section matches and category matches - if they do then you are in a service output loop - loop until category and or section changes.

At this point close any open <ul>'s and update the section and category vars.

There are a number of ways to do this - but as Ray says show us how far you have got.
0
 

Author Comment

by:mar2195
ID: 39200038
My 1st attempt... thinking that I need to first check if the "section" exists, then assign the current "section" to a "section_last" to compare the next "section" with the current "section" value ... and if not, print the "section" ... and then onto the category.  I didn't code the "service" value section because I wasn't haven't any success with the section & category values.  It seems that I'm having an issue with either the 'logic' behind this or maybe there's a method for comparing the past value in the loop with the next value that PHP offers that I'm missing.


<?php 
$section = '';
$category = '';
$service = '';
$section_last = '';
$category_last = '';
$service_last = '';

$x = 0;

$file = fopen("categories_data.txt", "r");
if (!$file) { echo 'ERROR: Unable to open file: <strong>'.$file.'</strong>'; }
fgets($file); // IGNORE FIRST LINE IN FLATFILE - column names

while (!feof($file) ) {
	$lines = fgets($file);
	$ele = explode('|', $lines);

	$section = $ele[0];
	$category = $ele[1];
	$service = $ele[2];

	$service = str_replace(array("\n", "\r", "\r\n", "\n\r"), '',$service);

	if(strlen($section)>0) {
		if(!($section == $section_last)) {
			echo '
	<ul>
		<li>'.$section;
			$section_last = $section;
			$x++;
		}
	}
	echo '
			<ul><li>'.$category.' > '.$service.'</li></ul>
';
		if($x) {
			if($section != $section_last) {
				echo '
		</li>
	</ul>
';
		}
	}


} // end $data_file WHILE

fclose($file);
?>

Open in new window

0
 
LVL 51

Accepted Solution

by:
Julian Hansen earned 500 total points
ID: 39200524
I was thinking more along these lines
<?php
define('TAB', "\t");
define('TAB2', "\t\t");

// Open the input file 
$file = fopen('lines.csv', 'rt');
// ignore the first line 
fgets($file);
// Get the next line into an array splitting on the pipe 
$line = fgetcsv($file, 255, '|');

// set the initial section value 
$section = $line[0];
// Initialise batch array
$current = array();
// Loop through all lines in the file 
do {
// If this line contains a section that is not the same as the previous line then dump the last section
  if ($section != $line[0]) {
    dump_categories($current, $section);
// Set our section flag to the next section    
    $section = $line[0];
// Clear the array of batched items to dump    
    $current = array();
  }
// Add the line to the batch
  $current[] = $line;
} while($line = fgetcsv($file, 255, '|'));
// Remember to dump batch that has built up
dump_categories($current, $section);
fclose($file);

function dump_categories(& $lines, $section)
{
// Set our category flag
  $category = $lines[0][1];
// initialise batch array
  $current = array();
// Start a new seection  
  echo '<ul class="section">' . PHP_EOL;
  echo TAB . '<li>' . $section . PHP_EOL;
// If there are categories in this section then process them - don't want empty bullets here  
  if (!empty($lines[0][1])) {
    foreach($lines as $l) {
// if this is a new category then dump category and services for this category    
      if ($category != $l[1]) {
        dump_services($current, $category);
// set our category flag        
        $category = $l[1];
// ... and clear the batch array        
        $current = array();
      }
// add to batch      
      $current[] = $l;
    }
// and dump batched lines    
    dump_services($current, $category);
  }
  echo TAB . '</li>' . PHP_EOL;
  echo '</ul>';
}
// Services we assume is the end of the line so output the service if it exists.
function dump_services(& $lines, $category)
{
  echo TAB2 . '<ul class="category">' . PHP_EOL;
  echo TAB . TAB2 . '<li>' . $category . PHP_EOL;
  if (!empty($lines[0][2])) {
    echo TAB2 . TAB2 . '<ul class="service">' . PHP_EOL;
    foreach($lines as $l) {
      if (!empty($l)) {
        echo TAB . TAB2 . TAB2 . '<li>' . $l[2] . '</li>' . PHP_EOL;
      }
    }
    echo TAB2 . TAB2 . '</ul>' . PHP_EOL;
  }
  echo TAB . TAB2 . '</li>' . PHP_EOL;
  echo TAB2 . '</ul>' . PHP_EOL;
}
?>

Open in new window

0
 

Author Comment

by:mar2195
ID: 39200578
@julianH ... YES YES YES!!!  As I thought, this was much more involved than I anticipated.  My logic was way too simple - thinking:  Just check the previous section/category/service against the new data in the loop.  So I never would've nailed this.

I can tell you that I searched high and low on the web for even an explanation/tutorial about this situation.  Flatfile help on the web is minimal vs other database coding.  I also GREATLY appreciate your explanations along with the code.  When I have a project such as this, I want to learn something rather than just COPYING/PASTING the code.

There are very specific issues using a flatfile as opposed to other database methods.  Other methods allow for nearly direct access to data - and for data comparisons.  "Holding" data for comparison seems a little more involved using a flatfile.

Thanks again.
0
 

Author Closing Comment

by:mar2195
ID: 39200581
AWESOME!!  I hope this question / solution makes it into the web's searches.  It's sorely needed!
0
 
LVL 51

Expert Comment

by:Julian Hansen
ID: 39200778
You are most welcome - and thanks for the points.
0
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 

Author Comment

by:mar2195
ID: 39200876
@julianH ...

One additional question...

If I want to edit/add things within the loop, where would I enter this info?

I've made a few attempts, but I can't seem to get "into" the loop correctly.

Meaning... if I want to add an incrementing $i for an ID number per category, where would that get added?  I can't figure out where to put the $i and the $i++ ??  Thanks.
0
 
LVL 51

Expert Comment

by:Julian Hansen
ID: 39200968
I would do it with a static variable in the dump_services function like so

function dump_services(& $lines, $category)
{
// Make the variable static so each time 
// we come back to this function the old value is remembered
  static $categoryid = 1;
// Add the ID and increment in the same operation
  echo TAB2 . '<ul class="category" id="' . sprintf("category%03d", $categoryid++) .'">' . PHP_EOL;
  echo TAB . TAB2 . '<li>' . $category . PHP_EOL;
...

Open in new window

0
 

Author Comment

by:mar2195
ID: 39201069
Thanks.  Since I needed the increment number throughout the loop for my jQuery code, I removed the category%03d and put the NUM var as needed in the loop and then put the ++ at the end of the function.  Works fine.  Thanks again.
0
 

Author Comment

by:mar2195
ID: 39201129
I noticed that I cannot access the $section variable/value from the dump_categories function in the dump_services.  Is there a way to make this variable global?
0
 
LVL 108

Expert Comment

by:Ray Paseur
ID: 39201333
You might want to post a new question about that.  You can make variables global with the PHP global keyword.  Whether it is a good or bad idea to make variables global is an entirely different line of reasoning, and most professional programmers would stay away from global variables.
0
 

Author Comment

by:mar2195
ID: 39201379
@Ray_Paseur ... I may have used the word global incorrectly.  I simply meant that I wanted the $section var value in the dump_categories function to be accessible within the dump_services function.  Thanks.
0
 
LVL 51

Expert Comment

by:Julian Hansen
ID: 39201711
Then you will still have to use the global keyword

Any variables declared outside the function are accessible only if declared global inside the function

function dump_categories( ... )
{
   global $section;
}

A way around this is as follows

function get_next_section()
{
   static $section = 0;
   return $section++;
}

Then you can get the next value of $section anywhere in the application

$next = get_next_section();
0
 

Author Comment

by:mar2195
ID: 39201738
Ah!  Yep.  That's what I was missing.  How do I get the NEXT.  Got it.  Thanks,
0

Featured Post

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

Suggested Solutions

Shoutout to Emily Plummer (http://www.experts-exchange.com/members/eplummer26.html) for giving me this article! She did most of it, I just finished it up and posted it for her :)    Introduction In a previous article (http://www.experts-exchang…
Introduction Since I wrote the original article about Handling Date and Time in PHP and MySQL (http://www.experts-exchange.com/articles/201/Handling-Date-and-Time-in-PHP-and-MySQL.html) several years ago, it seemed like now was a good time to updat…
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.
The viewer will the learn the benefit of plain text editors and code an HTML5 based template for use in further tutorials.

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now