I thought to implement advanced commenting system in my website using PHP-MySql. I finally settled for a 3-level comment-reply system for this purpose.
Well, for that purpose I came across this article to implement the data-structure of the SQL database.
I planned to use nested set mdoel for the feature I want to use. The structure of the comments are like this-
<ul>
<li>Parent comment</li>
<ul>
<li>First reply of parent comment</li>
<ul>
<li>reply of the previous reply</li>
<ul>
<li>reply of the previous reply</li>
<li>another reply of the previous reply</li>
</ul>
<li>another reply of the previous comment</li>
</ul>
<li>second reply of the parent comment</li>
</ul>
</ul>
For this type of structure, I have been playing around with PHP to show the query detecting the parents and its child uniquely(for fetching user details associated with each comment) and produce the output in the manner if shown above. Do anyone have idea how to do it. Please help me out.
EDIT :
I have a seperate user table in SQL linked to the comment table as user.id=comment.id. So considering this, what would be the recommended approach to detect user activity for each comment? I mean, for ex- I want to fetch user name and email for a sub-comment of parent comment 2. Hoow could it be done?
Use the query from "Finding the Depth of the Nodes", and this PHP code will create the nested lists.
$cur_depth = -1;
while ($row = mysqli_fetch_assoc($query)) { // Loop through results of query
if ($row['depth'] > $cur_depth) {
echo "<ul>\n";
$cur_depth = $row['depth'];
}
else while ($cur_depth > $row['depth']) {
echo "</ul>\n";
$cur_depth--;
}
echo "<li>" . $row['comment'] . "</li>\n";
}
while ($cur_depth > -1) {
echo "</ul>\n";
$cur_depth--;
}
Related
I'm having doubts about manipulating the child menu items to improve the display style.
I am currently generating according to documentation:
function index()
{
$list = $this->Categorias->find('threaded')->toArray();
$this->set('list', $list);
}
$renderItems = function($items) use (&$renderItems)
{
echo '<ul>';
foreach ($items as $item) {
echo '<li>';
echo h($item->name);
if ($item->children) {
$renderItems($item->children); // < recursion
}
echo '</li>';
}
echo '</ul>';
};
$renderItems($list);
But the display is in error.. The display is in this format:
I appreciate any comments!
As mentioned in the tree behavior docs, if you want to use threaded results, then you need some kind of recursive functionality to iterate not only over the top level items (which your example is doing), but also over the nested child items.
Also note that using the children finder as in your example, will only retrieve the children of the node with the specified primary key, ie it won't retrieve their parent node, also if the table contains multiple root nodes, they wouldn't be retrieved either. So depending on your data you might need to use only the threaded finder.
That being said, the children of an item are nested under the children property/key, so a basic example could look like this:
$renderItems = function($items) use (&$renderItems)
{
echo '<ul>';
foreach ($items as $item) {
echo '<li>';
echo h($item->name);
if ($item->children) {
$renderItems($item->children); // < recursion
}
echo '</li>';
}
echo '</ul>';
};
$renderItems($list);
That would create a nested list like this (without the formatting/indenting of course):
<ul>
<li>
Fun
<ul>
<li>
Sport
<ul>
<li>Surfing</li>
<li>Skating</li>
</ul>
</li>
</ul>
</li>
<li>
Trips
<ul>
<li>National</li>
<li>International</li>
</ul>
</li>
</ul>
See also
Cookbook > Database Access & ORM > Behaviors > Tree
Just getting into the whole MVC developing style.
Using CI I think I got the initial hang on the controllers, models and views. But, I run into a problem which was an easy-peasy for me in "my own" way of coding.
I have a single DB table to drive dynamic menu. The design is:
With sample data being:
The logic, in my head anyway, would be:
select * where active=1, then
if row's parent_id==0 check the retrieved data-set for rows where
parent_id==id of the current row
if not found, generate <li class="no_subs"></li> element
if found, generate <li class="subs"> element, open new <ul>
generate <li></li> for all items that matched, close <ul>, close
"subs" <li>
So, based on the above sample data the resulting html would be:
<li class="no_subs">Home</li>
<li class="no_subs">News</li>
<li class="subs">Training
<ul>
<li>Materials</li>
<li>Tests</li>
</ul>
</li>
<li class="subs">Other
<ul>
<li>Usefull links</li>
</ul>
</li>
So far all I can do is generate the top level menu.
Any ideas how to tackle this?
Thanks,
You can join the same table twice to achieve your goal
grub the data where parent_id == 0 and active == 1, so you will get the only menu titles.
join the data with the same table where id == parent_id. so you will get the this table.
id | name | parent_id | active | link | as_id | as_name | as_parent_id | as_active | as_link
**this 2 steps can be done in single sql query.
then i think this code will help you...
$sub_menu_started = false;
for ($i=0; $i < count($result); $i++) {
if (empty($result[$i]['as_name'])) {
echo '<li class="no_subs">'.$result[$i]['name'].'</li>';
}
else {
if(!$sub_menu_started) {
echo '<li class="subs">'.$result[$i]['name'];
echo '<ul><li>'.$result[$i]['as_name'].'</li>';
$sub_menu_started = true;
}
else {
if ($result[$i]['id'] == $result[$i-1]['id']) {
echo '<li>'.$result[$i]['as_name'].'</li>';
if($result[$i]['id'] != $result[$i+1]['id']) {
echo '</ul></li>';
$sub_menu_started = false;
}
}
}
}
}
if ($sub_menu_started) {
echo '</ul></li>';
}
I am developing a website using PHP and this is the 1st time I am trying a dynamic menu.
What I am doing is I have created a table for pages.
The table structure is as follows:
____________________________________________________________________________
|page_id|title|url|content|menu_title|show_on_navuigation|is_sub|parent_page|
|_______|_____|___|_______|__________|___________________|______|___________|
Here is_sub indicates whether it is a dropdown menu of some main menu. and parent_page is the main menu of the sub menu.
Now, what I want is, like
I have created 2 parent pages:
About Us and Test
and 2 sub menu's: Test aboutus and testsub,
Test aboutus is sub of About Us and testsub is sub of test.
How actually should the query and the loop, so that the menu renders perfectly.
Thanks in advance.
This is my menu structure:
<ul>
<li class='has-sub'><a href='#'><span>About Us</span></a>
<ul>
<li><a href='corporateprofile'><span>Corporate Profile</span></a></li>
<li class='last'><a href='visionandmission'><span>Vision & Mission</span></a></li>
</ul>
</li>
<li class='has-sub'><a href='#'><span>Business Services</span></a>
<ul>
<li><a href='recruitment'><span>Recruitment</span></a></li>
<li><a href='training'><span>Training</span></a></li>
<li><a href='executivesearch'><span>Executive Search</span></a></li>
<li><a href='payroll'><span>Payroll</span></a></li>
<li class='last'><a href='backgroundverification'><span>Background Verification</span></a></li>
</ul>
</li>
<li class='has-sub'><a href='#'><span>Employers</span></a>
<ul>
<li><a href='enquiry'><span>Enquiry</span></a></li>
<li><a href='jobdescription'><span>Job Description</span></a></li>
<li><a href='employercontract'><span>Employer Contract</span></a></li>
<li class='last'><a href='feedback'><span>Feedback</span></a></li>
</ul>
</li>
<li class='has-sub'><a href='javascript:;'><span>Job Seeker</span></a>
<ul>
<li><a href='applyforjob'><span>Apply For Job/Register</span></a></li>
<li><a href='careertips'><span>Career Tips</span></a></li>
<li><a href='interview Questions'><span>Interview Questions</span></a></li>
<li><a href='interviewprocess'><span>Interview Process</span></a></li>
<li class='last'><a href='corporatedress'><span>Corporate Dress</span></a></li>
</ul>
</li>
<li class='has-sub'><a href='#'><span>Franchise</span></a>
<ul>
<li class='last'><a href='#'><span>Franchise Enquiry Form</span></a></li>
</ul>
</li>
<li class='last'><a href='#'><span>Contact us</span></a></li>
</ul>
<?php
function displayMenu($parent_page_id) {
$sql = "SELECT * FROM `pages` WHERE `parent_page` = '$parent_page_id'"; // sql
$result = mysql_query($sql);
if( mysql_num_rows($result) === 0 ) { // mysql_num_rows() is deprecated, but you are using mysql extension so that's why I show it here
return true; // exit function
} // else, continue to rest of function
echo '<ul>';
while($row = mysql_fetch_assoc($result)) { // deprecated mysql php function here for simplicity
echo '<li>' . $result['menu_title'] . ''; // no closing </li> yet
displayMenu($row['page_id']); // this is the recursive part
echo '</li>'; // close the <li> from before the recursion
}
echo '</ul>';
}
$get_base_menu = mysql_query("SELECT * FROM pages WHERE parent_page = 0");
while($fetch_base_menu = mysql_fetch_array($get_base_menu))
{
$parent_page_id = $fetch_base_menu['page_id'];
displayMenu($parent_page_id);
}
?>
I highly suggest trying to get away from using an Adjacency List model and move toward a much easier to manage solution, such as a nested set. Using an MPTT type solution should help you manage your hierarchical data much easier. Using an Adjacency List model you are limited at a certain point.
I'd suggest looking into using something along the lines of Zebra_MPTT, or some other form of MPTT library. Please checkout this article on Managing Hierarchical data in MySQL.
I think the simplest thing for you to code will be something like this recursive function. All you have to do is put it on your page and make sure you have parent_page_id = 0 for the base level menu items. (I'm using parent_page_id instead of parent_page because I assume that is what you mean and it is more clear for the answer.)
Calling the function with an argument of "0" should give you the full menu tree.
$trace = array();
function displayMenu($parent_page_id) {
$sql = "SELECT * FROM `tbl_menu` WHERE `parent_page_id` = $parent_page_id"; // sql
$result = mysql_query($sql);
if( mysql_num_rows($result) === 0 ) { // mysql_num_rows() is deprecated, but you are using mysql extension so that's why I show it here
$trace[] = "Page_id $parent_page_id has no children";
return true; // exit function
} // else, continue to rest of function
echo '<ul>';
while($row = mysql_fetch_assoc($result)) { // deprecated mysql php function here for simplicity
$trace[] = "Entered while loop for page_id = $parent_page_id"; // not an error, just tracking
echo '<li>' . $row['menu_title'] . ''; // no closing </li> yet
displayMenu($row['page_id']); // this is the recursive part
echo '</li>'; // close the <li> from before the recursion
}
echo '</ul>';
}
displayMenu(0); // call function for base level
var_dump($trace);
More info:
I have researched the topic before and had a nice link to a page on mysql.com with an in-depth exploration of hierarchical relational database structures. But when I checked, the link was broken!
But, I think I found it on another site, here:
http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/
I'm attempting to make my own CMS and one thing I wanted to do was make it so I could reorder and change the navigation elements in the CMS. I found a plugin called NestedSortable that let me do this. I used it and had a lot of trouble with it, so I got my expert PHP friend to help me do it. She sat down, struggled for a bit and then got it to work.
Well, the idiot I was, when I got home later that day I sat down, took my local copy, added a something to it and uploaded it to the server... not realizing I had forgotten to download the newer version! So poof, all my friend's work is gone and I'm stuck with a navigation adjustment that doesn't work.
I lost the index.php (that displays it on the page) file but I saved the nav_adjust.php (that inputs it into the database). All I want is to be able to re-order the navigation and send it to the database to be displayed correctly on refresh. The information is send to nav_adjust.php when the submit button is pressed.
The only limitation is that the navigation can only have 3 levels to it (primary, child and grandchild). My navigation looks like when they're all primary. I have alerted back the echo for all the images so you can see what nav_adust.php is doing:
(Please note the array values.)
This is as if there was one child on a parent:
This is as if there was two grandchildren on a child:
And this is mixed:
My index.php file, which doesn't work anymore, looks like this:
<form method="post" action="nav_adjust.php"> <!--This form posts to nav_adjust.php-->
<ol id="pageList">
<?php
$nav = mysql_query("SELECT * FROM `".$name['Website']);
$num = 0;
while( $col_nav = mysql_fetch_assoc($nav) ){
if ($col_nav['orderId'] != 0) {
echo "<li id='list_".$col_nav['id']."'>
<div>Example Title</div>";
} else {
echo "<ol>
<li id='list_".$col_nav['id']."'>
<div><h1>Example Title</div>";
</li>
</ol>\n";
}
echo "</li>\n";
++$num;
}
?>
</ol>
<button type='submit'>Update</button>
</form>
<script>
$(document).ready(function(){
$('#pageList').nestedSortable({
/*option that don't matter*/
});
$("button").on('click', function(event){
serialized = $('ol#pageList').nestedSortable('serialize');
$.post( "nav_adjust.php", serialized, function( data ) {
alert( data );
});
return false;
});
});
</script>
Again, the above code doesn't work. Instead of making a list item look like this:
<ol>
<li>
<ol>
<li></li>
...
</ol>
</li>
...
</ol>
It makes it look like this (note the closed li tag before the ol):
<ol>
<li></li>
<ol>
<li></li>
</ol>
...
</ol>
And this is the nav_adjust.php that my friend did for me which works beautifully if given the right information from index.php
$list = $_POST['list'];
$num = 1;
foreach ($list as $key => $value ) {
if ($value == "null") {
//$value = $num;
mysql_query("UPDATE `myTable` SET orderId=".$num.", parent='null' WHERE id=".$key."");
$text .= "UPDATE `myTable` SET orderId=".$num.", parent='null' WHERE id=".$key."\n\n";
} else {
$query = "UPDATE `myTable` SET orderId=". $num .", parent=".$value;
$text .= "UPDATE `myTable` SET orderId=". $num .", parent=".$value."\n\n";
$var = $list[$value];
if ( $var != null && $list[$var] != null ) {
$query .= ", grandchild = 1";
}
$query .= " WHERE id=".$key;
mysql_query($query);
}
$num++;
}
echo "Content is below\n";
print_r( $_POST['list']);
echo $text;
So, as a recap, I have a navigation that submits serialized data from index.php to nav_adjust.php which then submits it into a database (and echos it back so I know what I did). Then index.php, upon refresh, should keep the list items in place, with all children as children and such. But I overwrite index.php and have no idea how my friend did it.
My table fields are (before I forget):
id, title, orderId (parent), parent (child) and grandchild.
I was meaning to change the names of orderId and parent to make it less confusing but I never did :(
If somebody could help me, I'd be so very thankful.
If there is any other information I can give you, please ask!
After hours of struggling, I had the genius idea of asking GoDaddy if they backed up files and directories, and they did! So I got my file back. For anybody interested, the code missing on index.php looked like this (where $name['website'] is 'myTable'):
$parents = mysql_query("SELECT * FROM `".$name['Website']."` WHERE parent = 0 ORDER BY `orderId` ASC");
while($parent = mysql_fetch_array($parents)){
echo "<li id='list_".$parent['id']."'>
<div>
<span class='disclose'>
<span></span>
</span>
<span class='title'>
<a href='mod.php?id=".$parent['id']."'>".stripslashes($parent['page'])."</a>
</span>
</div>";
// Get chil elements
$children = mysql_query("SELECT * FROM `".$name['Website']."` WHERE parent = " . $parent['id'] . " ORDER BY `orderId` ASC") or die(mysql_error());
while($child = mysql_fetch_array($children)){
echo "<ol>
<li id='list_".$child['id']."'>
<div>
<span class='disclose'>
<span></span>
</span>
<span class='title'>
<a href='mod.php?id=".$child['id']."'>".stripslashes($child['page'])."</a>
</span>
</div>";
# Get grandchild elements
# (Please consider making an recursive function or so out of this if you plan to have grand grand children etc.,
# Because if you create new whiles manually for every instance, this is gonna look pretty darn ugly.
# Also, it makes it impossible to have an unknown depth.)
$grandchildren = mysql_query("SELECT * FROM `".$name['Website']."` WHERE parent = " . $child['id'] . " AND grandchild = 1 ORDER BY `orderId` ASC");
while($grandchild = mysql_fetch_array($grandchildren)){
echo "<ol>
<li id='list_".$grandchild['id']."'>
<div>
<span class='disclose'>
<span></span>
</span>
<span class='title'>
<a href='mod.php?id=".$grandchild['id']."'>".stripslashes($grandchild['page'])."</a>
</span>
</div>
</li>
</ol>\n";
}
// Close child
echo "</li>
</ol>\n";
}
// Close parent
echo "</li>\n";
}
?>
I have a categories table that looks like this:
----------------------------------------
| id | parentId | Name |
----------------------------------------
1 0 Cat 1
2 0 Cat 2
3 0 Cat 3
4 2 Cat 4
5 3 Cat 5
6 5 Cat 6
Basically I need to iterate through the categorys creating a UL LI html list like the following:
<ul id="categories">
<li id="1">Cat 1</li>
<li id="2">Cat 2
<ul>
<li id="4">Cat 4</li>
</ul>
</li>
<li id="3">Cat 3
<ul>
<li id="5">Cat 5
<ul>
<li id="6">Cat 6</li>
</ul>
</li>
</ul>
</li>
</ul>
Im having major issues trying to iterate over this trying to create the above html. The id's maybe any number of levels deep within parentId's. Im donig this in PHP. Because there are nth number of levels deep I think I need to do some kind of array_walk funuction but not surch how. Also to make things just a little harder the machine it sists on runs PHP4 and I know it needs upgrading but it cant at the min so I need a php 4 solution ideally. How should I go about doing this?
Try the left/right tree method for storing hierarchical information in a database.
http://blogs.sitepoint.com/hierarchical-data-database/
This is what I do in my website, where I have multi-level LIs that need to open at 1:6 and have children 2:3,4:5 where the first number is the 'left' and the second is the 'right'. I have about 5 levels deep at the moment, but you could have many more. It's just a matter of developing an interface for setting the correct left/right values based on the position you add it to.
You just have to add a 'lft' and 'rgt' column to your table (as explained in that article).
First create a tree structure and insert your categories into the tree using id and parent_id. Then try Depth-first_search using either a list of references to arrays to be processed or recursion.
function printRecList($tree){
// exit condition
if (is_string($tree))
echo "<li>$tree</li>";
echo "<ul>";
foreach ($tree as $subtree)
printRecList($subtree); // recursion step
echo "</ul>";
}
The way your database is structured, you cannot do that with a single mysql query and you should do it recursively. Something in line with:
function print_children ($id) {
$children = query("SELECT * FROM `table` WHERE `parentId` = " . (int)$id);
if (!empty($children)) {
echo '<ul>';
foreach ($children as $child) {
echo '<li>' . $child['name'];
print_children($child['id']);
echo '</li>';
}
echo '</ul>';
}
}
print_children(0);
Replace query with something that gets results for your database query.
function writelevel($id, $txt, $children) {
if (isset($txt[$id]))
echo "<li id=\"$id\">".$txt[$id];
if (isset($children[$id])) {
echo "<ul>";
foreach ($children[$id] as $child)
writelevel($child, $txt, $children);
echo "</ul>";
}
if (isset($txt[$id]))
echo "</li>";
}
//Assuming your query is done and the result is in $qry
$txt=array();
$children=array();
//Fetch and structure data
while (true) {
//Fetch next row
$row=mysql_fetch_row($qry);
if (!$row) break;
//Store text
$txt[$row[0]]=$row[2];
//Store child relationships
if (!isset($children[$row[1]])) $children[$row[1]]=array();
$children[$row[1]]=$row[0];
}
//Writeout
writelevel(0);