Jump to content

How can I make a proper multi-level thread using parent-child? ...


Recommended Posts

Mmm i'm not sure if i'm approaching this correctly.

 

Anyways, I wanted to create a multi-level thread, Where the child thread would be after the parent thread.

 

Something like this:

 

Hello World

      - Re: Hello World

              - Hi!

              - What's up?

      - Hi Bob!

Goodbye World

      - ....This guy got issues

      - Dude! You should watch Despicable Me! "It's so fuzzy i'm going to dieee!"

 

 

Anyways, so my table looks something along the line of this:

 

|    post id  |    parent_id    |            title            |      body    |  date      |

      1                |      NULL        |    Hello                  | Hi, i'm K    |  date

      2                |      NULL        |    Thread One      | Body One  |  date

      3                |          1          |    Re: Hello World | Body One  |  date

      4                |          2          |    Thread post 4  | Body One  |  date

      5                |          1          |    Thread post 5  | Body One  |  date

      6                |          1          |    Thread post 6  | Body One  |  date

      7                |          3          |    Hi What's up    | Body One  |  date

 

 

And here's the PHP code, although it's not very neat :( I want to clean it up and make it neater.  I can only go two level deep so far.  Any suggestion or recommendations? 

 

Using while & for:

 

// Select * Threads
$q = "SELECT * FROM test_db ORDER BY post_id ASC";
$r = @mysql_query($q);



while ($pid_row = @mysql_fetch_object($r)) {

// Array
$pid_arr[] = $pid_row->post_id; // Assign Indexes to Post ID
$ppid_arr[] = $pid_row->parent_id; // Assign Indexes to ParentID
$md_arr = array($pid_arr, $ppid_arr);  // Create multi-dimensional array matrix

$parent_id = $pid_row->parent_id;




}

for ($i = 0; $i < count($pid_arr); $i++) {

for ($a = 0; $a < count($ppid_arr); $a++) {
		$pid = $pid_arr[$i]; // pid = post id
		$ppid = $ppid_arr[$i]; // ppid = parent id

		//echo "<div>~~ PID $pid ~~ PPID $ppid ~~</div>";

		if ($ppid === NULL) {
			$ppid_is_null = " & parent_id is NULL";
		} else {
			$ppid_is_null = NULL;
		}

		// Select Top level Thread
		$z = "SELECT * FROM test_db WHERE post_id = '$pid'";
		$y = @mysql_query($z);

		while ($t_row = @mysql_fetch_object($y)) 
		{

		$title = $t_row->title;	 // title of child thread
		$level = $t_row->level;  // level of child thread...is this useful?
		}




		// Display top level thread lvl 1
		if ($ppid === NULL) {  // If the parent_id is NULL, display top level thread
			echo	'<div>PID: ' . $pid . ' || PPID: ' . $ppid . " || Title: " . $title . "</div><br>\n";	

			$post = $pid; // PostID = 1
			$parent = $ppid; // Parent ID ex: [1]

			// Get child id, where parent id = [1]
			// trying to get the child thread here...but it's not working
			$child_q = "SELECT * FROM test_db WHERE parent_id = '$post'";
			$child_r = @mysql_query($child_q);

			while($c_row = @mysql_fetch_object($child_r)) {
				$c_arr[] = $c_row->post_id;
				$c_ppid_arr[] = $c_row->parent_id;
			}
				for ($n = 0; $n < count($c_arr); $n++) {
					if ($c_ppid_arr[$n] === $post) {
						echo "<div>$c_arr[$n]</div>";	



					}

				}
				break;

			//echo "<div>PID: $pid</div>";




		} 




		break;
}

}	

 

 

Using While:

<?php 
require_once('db_connection');
echo '<hr><hr><br>';

// I just realized that selecting * where parent id is null might limit the amount of rows retrieved.

// Query to select threads
$n = NULL;
$q = "SELECT * FROM test_db WHERE (test_db.parent_id is NULL) Order By date ASC";
$r = @mysql_query($q);

while ($threads = @mysql_fetch_object($r)){
$pid = $threads->post_id;
$sid = $threads->subject_id;
$parent_id = $threads->parent_id;
$title = $threads->title;
$body = $threads->body;



echo '<div>PostID: ' . $pid . ' || Parent: ' . $parent_id . ' || SubjectID: ' . $sid . ' || Title: ' . $title . ' || Body: ' . $body . '</div>';

	$q2 = "SELECT * FROM test_db WHERE parent_id = '$pid' Order by date DESC";
	$r2 = @mysql_query($q2);

	while ($level2 = @mysql_fetch_object($r2)) {
	$level2_pid = $level2->post_id;
	$level = 1.5 *($level2->level); // lvl 2: 3em


	echo '<div style="text-indent:' . $level . 'em">' . 'PostID: ' . $level2->post_id . ' || ParentID: ' . $level2->parent_id . '</div>';


					$q3 = "SELECT * FROM test_db WHERE parent_id = '$level2_pid'";
					$r3 = @mysql_query($r3);
					while ($level3 = @mysql_fetch_object($r3)) {

					$level3 = 1.5 + $level;

					echo '<div style="text-indent:' . $level3 . 'em">' . 'PostID: ' . $level3->post_id . ' || ParentID: ' . $level3->parent_id . '</div>';

					}






	}


}


?>

 

I'm not a comp. sci major or anything, so I'm learning this as I go.  Any help would be greatly appreciated.  It seems like the while & for is more practical, but i'm not quiet sure.  What am I doing wrong?

 

- K

 

:D I just joined this forum today ^_^  Hope to be good friends with you guys and gals :D

If you notice, your "q3" (level 3) code is exactly the same as your "q2" (level 2) code, except you had to use different variable names.  This is a great opportunity for a function!  You are going to have to write a recursive function here.  That's just a normal function that calls itself.  So, after you output your top level posts (parent_id IS NULL) you would call a function to show its children. The function would output each child and call the function to output its children.  Eventually, you should reach a point where there are no more children and it will unwind.  Something along these lines:

 

function showChildrenOf($parent_id) {
  $qry = "SELECT * FROM test_db WHERE parent_id = '$parent_id' Order by date DESC";
  $res = @mysql_query($qry);

  while ($levelObj = @mysql_fetch_object($res)) {
    $level_pid = $levelObj->post_id;
    $level = 1.5 *($levelObj->level); // lvl 2: 3em

    echo '<div style="text-indent:' . $level . 'em">' . 'PostID: ' . $levelObj->post_id . ' || ParentID: ' . $levelObj->parent_id . '</div>';
    showChildrenOf($levelObj->post_id);
}

 

Note: I just copied your code and made minor changes. I do not recommend or endorse the user of the '@' operator.  You need to check $res before the while loop to make sure it has a value.

The second code you posted does 3 levels. Period. But the second and third levels are basically the same code, so you could keep copying it to make it how ever many levels you want. If you replace the second and third levels with a call to the function I showed, it will go to an infinite number of levels (theoretically - you will be limited by memory and PHP timeout constraints).

 

function showChildrenOf($parent_id) {
  $qry = "SELECT * FROM test_db WHERE parent_id = '$parent_id' Order by date DESC";
  $res = @mysql_query($qry);

  while ($levelObj = @mysql_fetch_object($res)) {
    $level_pid = $levelObj->post_id;
    $level = 1.5 *($levelObj->level); // lvl 2: 3em

    echo '<div style="text-indent:' . $level . 'em">' . 'PostID: ' . $levelObj->post_id . ' || ParentID: ' . $levelObj->parent_id . '</div>';
    showChildrenOf($levelObj->post_id);
}

require_once('db_connection');
echo '<hr><hr><br>';

// I just realized that selecting * where parent id is null might limit the amount of rows retrieved.

// Query to select threads
$n = NULL;
$q = "SELECT * FROM test_db WHERE (test_db.parent_id is NULL) Order By date ASC";
$r = @mysql_query($q);

while ($threads = @mysql_fetch_object($r)){
$pid = $threads->post_id;
$sid = $threads->subject_id;
$parent_id = $threads->parent_id;
$title = $threads->title;
$body = $threads->body;

echo '<div>PostID: ' . $pid . ' || Parent: ' . $parent_id . ' || SubjectID: ' . $sid . ' || Title: ' . $title . ' || Body: ' . $body . '</div>';

showChildrenOf($parent_id);
}

 

Note: Again, I just copied your code there and I do not have any way of testing it here, but it should get you started down the right road.

 

 

The '@' operator hides error messages from you.  When applied to a statement, if the statement generates any errors, those errors will not be caught or reported.  In my opinion, it is better to leave it out, correct any errors during development as they occur, and test the inputs before a function call and the outputs after a function call and handle any exceptions in a user-friendly manner.

Hiya,

 

I used this one and it works well, but I run into some other issues.

 

Here's the php script


function showChildrenOf($parent_id) {
  $qry = "SELECT * FROM test_db WHERE parent_id = '$parent_id' Order by date DESC";
  $res = @mysql_query($qry);

  while ($levelObj = @mysql_fetch_object($res)) {
    $level_pid = $levelObj->post_id;
    $level = 2 *($levelObj->level); // lvl 2: 3em
//echo 'test';
    

//if ($level_pid === $parent_id) {
echo '<div style="text-indent:' . $level . 'em">' . 'PostID: ' . $levelObj->post_id . ' || ParentID: ' . $levelObj->parent_id . '</div>';
//}
   
showChildrenOf($levelObj->post_id);
}

}
//require_once('db_connection');
echo '<hr><hr><br>';

// I just realized that selecting * where parent id is null might limit the amount of rows retrieved.

// Query to select threads

$q = "SELECT * FROM test_db WHERE (test_db.name_id = '1427') Order By date ASC";
$r = @mysql_query($q);

while ($threads = @mysql_fetch_object($r)){
$pid = $threads->post_id;
$sid = $threads->subject_id;
$parent_id = $threads->parent_id;
$title = $threads->title;
$body = $threads->body;

if ($parent_id === NULL) {
echo '<div>PostID: ' . $pid . ' || Parent: ' . $parent_id . ' || SubjectID: ' . $sid . ' || Title: ' . $title . ' || Body: ' . $body . '</div>';

}
showChildrenOf($parent_id);

}

 

What gets outputted is this: http://cbbd.org/db_test4.php

 

[attachment deleted by admin]

I did a slight modification:

function showChildrenOf($parent_id) {
  $qry = "SELECT * FROM test_db WHERE parent_id = '$parent_id' Order by date DESC";
  $res = @mysql_query($qry);

  while ($levelObj = @mysql_fetch_object($res)) {
    $level_pid = $levelObj->post_id; // Gets PostID
    $level = 1.75 *($levelObj->level); // lvl 2: 3em
//echo 'test';
    

//if ($level_pid === $pid) {
echo '<div style="text-indent:' . $level . 'em">' . 'PostID: ' . $levelObj->post_id . ' || ParentID: ' . $levelObj->parent_id . '</div>';
//}
   
showChildrenOf($levelObj->post_id);
}

}
//require_once('db_connection');
echo '<hr><hr><br>';

// I just realized that selecting * where parent id is null might limit the amount of rows retrieved.

// Query to select threads

$q = "SELECT * FROM test_db WHERE (test_db.name_id = '1427') Order By date ASC";
$r = @mysql_query($q);

while ($threads = @mysql_fetch_object($r)){
$pid = $threads->post_id;
$sid = $threads->subject_id;
$parent_id = $threads->parent_id;
$title = $threads->title;
$body = $threads->body;

if ($parent_id === NULL) {
echo '<div>PostID: ' . $pid . ' || Parent: ' . $parent_id . ' || SubjectID: ' . $sid . ' || Title: ' . $title . ' || Body: ' . $body . '</div>';

showChildrenOf($pid); // $pid instead of $parent_id
}


}

 

 

Link: http://www.cbbd.org/db_test4.php

 

Now, is there an easy way to post a reply thread and assign the correct message level? mm

With a single button to post a reply, how are you going to know which entry they are replying to? If you want to allow replies directly from the list that you have now. You need to add a link at the end of each list entry.  It would be something like:

 

<A href="replyPage.php?replyto=idOfTopic&level=levelOfTopicPlus1">Reply</A>

 

replyPage.php would then use $_GET['replyto'] to get the ParentID of the new post; and use $_GET['level'] to get the level of the new post (make sure you have already added 1 to the topic's level when you build the link (or don't and add it in the reply page)).

 

Actually, if you are only using the level for the output indentation, you don't need it in the database. You can pass it to the initial call of showChildrenOf() and then each resursive call would pass the level it got plus 1 to the next call. Something like this:

 

function showChildrenOf($parent_id, $level_in) {
  $qry = "SELECT * FROM test_db WHERE parent_id = '$parent_id' Order by date DESC";
  $res = @mysql_query($qry);

  while ($levelObj = @mysql_fetch_object($res)) {
    $level_pid = $levelObj->post_id; // Gets PostID

    // INSTEAD OF THIS LINE
    // $level = 1.75 *($levelObj->level); // lvl 2: 3em
    // USE THIS LINE
    $level = 1.75 * ($level_in);

    //echo 'test';
    //if ($level_pid === $pid) {

    echo '<div style="text-indent:' . $level . 'em">' . 'PostID: ' . $levelObj->post_id . ' || ParentID: ' . $levelObj->parent_id . '</div>';

    //}
    showChildrenOf($levelObj->post_id, $level_in + 1);	
  }
}
//require_once('db_connection');
echo '<hr><hr><br>';

// I just realized that selecting * where parent id is null might limit the amount of rows retrieved.

// Query to select threads

$q = "SELECT * FROM test_db WHERE (test_db.name_id = '1427') Order By date ASC";
$r = @mysql_query($q);

while ($threads = @mysql_fetch_object($r)){
$pid = $threads->post_id;
$sid = $threads->subject_id;
$parent_id = $threads->parent_id;
$title = $threads->title;
$body = $threads->body;

if ($parent_id === NULL) {

echo '<div>PostID: ' . $pid . ' || Parent: ' . $parent_id . ' || SubjectID: ' . $sid . ' || Title: ' . $title . ' || Body: ' . $body . '</div>';

showChildrenOf($pid, 1); // $pid instead of $parent_id
}

}

 

By the way, this code is not producing anything different than selecting from the table with parent_id IS NULL. You are skipping the ones that are null, so you may as well just not select them. Performance would be better as the database grows. Also, you probably want an INDEX - not unique - on the parent_id column to help with performance down the road.

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.