Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 360
  • Last Modified:

PHP - Create UL Nested Lists From Flatfile

(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
mar2195
Asked:
mar2195
  • 8
  • 5
  • 2
1 Solution
 
Ray PaseurCommented:
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
 
Julian HansenCommented:
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
 
mar2195Author Commented:
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
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
Julian HansenCommented:
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
 
mar2195Author Commented:
@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
 
mar2195Author Commented:
AWESOME!!  I hope this question / solution makes it into the web's searches.  It's sorely needed!
0
 
Julian HansenCommented:
You are most welcome - and thanks for the points.
0
 
mar2195Author Commented:
@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
 
Julian HansenCommented:
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
 
mar2195Author Commented:
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
 
mar2195Author Commented:
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
 
Ray PaseurCommented:
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
 
mar2195Author Commented:
@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
 
Julian HansenCommented:
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
 
mar2195Author Commented:
Ah!  Yep.  That's what I was missing.  How do I get the NEXT.  Got it.  Thanks,
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 8
  • 5
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now