Solved

PHP - Create UL Nested Lists From Flatfile

Posted on 2013-05-27
15
348 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 8
  • 5
  • 2
15 Comments
 
LVL 110

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 56

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
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!

 
LVL 56

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 56

Expert Comment

by:Julian Hansen
ID: 39200778
You are most welcome - and thanks for the points.
0
 

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 56

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 110

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 56

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

Announcing the Most Valuable Experts of 2016

MVEs are more concerned with the satisfaction of those they help than with the considerable points they can earn. They are the types of people you feel privileged to call colleagues. Join us in honoring this amazing group of Experts.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

These days socially coordinated efforts have turned into a critical requirement for enterprises.
3 proven steps to speed up Magento powered sites. The article focus is on optimizing time to first byte (TTFB), full page caching and configuring server for optimal performance.
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.

740 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