Recurrsive Tree total child count. This is not binary tree child PHP - php

I 'm working on a project in which data is stored in recursive way form.
There makes a tree like this.
I want to get total sub child count at each node.
I have root parent_id 123456.
I got direct child and there Id's by This code.
$parentcode = "Select ref_id from Total_childs where parent_id = '" . $exactcode . "'";
$code2 = Run($parentcode);
$data = array();
if (mysql_num_rows($code2) > 0) {
while ($rowcont = mysql_fetch_object($code2)){
$data[] = $rowcont->ref_id;
}
}
data[0]
data[1]
data[2]
data[3] shows direct child.
I want to get the total count of grand child at each node.?
This code sows the count of direct child.
$qry_t_childs = "SELECT COUNT(*) FROM Total_childs where parent_id = '" . $exactcode . "' ";
$qry_total_childs = Run($qry_t_childs);
if (mysql_num_rows($qry_total_childs) > 0) {
while ($rowfortotalchilds = mysql_fetch_array($qry_total_childs)) {
$tot_childs_first = $rowfortotalchilds[0];
$total_childs_first = $tot_childs_first;
}
}
This code would repeatedly But not working for more than one child in loop. Any possible way to get out of this?
Here is my Table structure.
I implement this code within "Do-while" loop so this shows counting for 2nd level. But not for whole tree counting.?

Related

Getting total count of nodes in tree

I am creating a simple Affiliate System (5 Level max). So basically the database have this kind of structure:
"aff level" column is just extra things so that i know that i listed out correctly.
I have successfully listed out (in bullet format) the parent & child of each agent. Below is my code:
function list_current_agents($aff_parent_id, $aff_level){
$max_level = 5;
if($aff_level <= 5)
{
$query = "SELECT aff_id, agent_code FROM affiliate WHERE aff_parent_id = '$aff_parent_id' AND aff_level = '$aff_level'";
$result = db_query($query);
$row = $result-> fetch_object();
if($result-> num_rows > 0)
{
echo '<ul>';
do{
$aff_id = $row-> aff_id;
$agent_code = $row-> agent_code;
echo '<li>';
echo $aff_id .' - '.$agent_code . ' - (level '.$aff_level.')';
echo '</li>';
$aff_level = $aff_level+1;
if($aff_level <= 5)
{
list_current_agents($aff_id, $aff_level+1);
}
}while($row = $result-> fetch_object());
echo '</ul>';
}
}
return $count;
}
and the output will be like this:
And now im stucked where i want to list out the total number of the child below if i select one of the parent agent. For example, if i select:
Agent1
total child : 8
Agent3
total child : 3
I have tried any method i can think of but cant work out with the logic.
Any help is very much appreciated.
thanks in advance.
Create an actual tree data structure and it will be easy to count the children.
Or you can use one of the many available on Github. This one is even made for you specific database structure.

How to display all posts under main category and its subcategories?

I have a custom php/mysql blog. In single post page, I have a sidebar where I am displaying categories. I want to display number of posts those belong to a main category and its subcategories in brackets as we usually see in WordPress. For now I have
Music
Pop
Nature
Lakes
Sea
I want to achieve
Music (5)
Pop (3)
Nature (8)
Lakes (2)
Sea (4)
As you see Music category has 5 posts under it and Pop has 3. But it doesn't mean there are 8 posts in reality. There are actually only 5 posts because Pop is subcategory of main category Music. 2 posts are directly under the main category Music while 3 posts are falling in its subcategory Pop. Thus 2 + 3 = 5.
The categories table looks as (Note that it has nth level of subcategories):
The posts table looks as (Note that a particular post can have its only one category parent i.e. a post can not have its multiple parents)
My code to fetch categories and their subcategories is
public function get_category_hierarchy(&$output, $parent_url, $parent = 0)
{
$sql = "SELECT * FROM `tb_categories` WHERE `category_parent` = " . $parent . " ORDER BY `category_name`";
$rows = $this->get_by_sql($sql);
// Removing trailing slash
$parent_url = rtrim($parent_url, "/");
if(is_array($rows))
{
foreach($rows as $row) :
$url = $parent_url . "/" . $row->category_url;
$output.= "<li><a href='" . $url . "'>" . $row->category_name . "</a></li>";
if($row->category_id != $parent)
{
$this->get_category_hierarchy($output, $url, $row->category_id);
}
endforeach;
}
return $output;
}
On sidebar, I am displaying these categories by following code:
$output = '';
// Create post object
$post_obj = new Post();
echo $post_obj->get_category_hierarchy($output, SITE_URL . 'category');
Now I'm wondering what code need to be add for fetching number of posts (post counts) that fall under a main category and subsequently its sub-sub-categories? Though I tried to fetch them but the result didn't come accurate.
Thank you in advance.
EDIT:
I modified function get_category_hierarchy() by doing following amendment:
foreach($rows as $row) :
$url = $parent_url . "/" . $row->category_url;
// Build database query
$sql = "SELECT COUNT(*) AS 'total_posts' FROM `tb_posts` WHERE `post_parent` = " . $row->category_id . " GROUP BY `post_parent`";
// Execute database query
$rows = $this->get_by_sql($sql);
$output[] = "<li><a href='" . $url . "'>" . $row->category_name . " (" . $rows[0]->total_posts . ")</a></li>";
But oops! It is giving me following result:
Music (2)
Pop (3)
Nature (2)
Lakes (2)
Sea (4)
Obviously, the parent category is having total number of post which are directly associated to it and not to its children. How to fix it?
The approach that I could think of :
Do a group by on the posts table on the post_parent.
From that form an associative array with the index as category_id and pair value as post_count.
Ex. $category_post_counts = array("4" => 5, "5" =>
10)
Now run a foreach loop for all the category ids and fetch its
corresponding parent.
Update the post count by using the indexes.
Suppose for category id 4(pop) there were 5 posts and in music there
were 10 posts. Now in the loop if the category parent is anything
else apart from 0 then update the post count in the array.
Ex.
$category_post_counts["<category_parent_id>"] =
$category_post_counts["<category_parent_id>"] +
$category_post_counts["<category_child_id>"]
This way your end result
will be an associative array with the category ids and all the post
counts.
Pseudo Code
//exctracting all the data
$rows = execute_this_query("SELECT COUNT(post_id) AS `post_count`, `post_parent` FROM `posts` GROUP BY `post_parent`");
//forming the associative array
foreach ($rows as $value) {
$associative_array[value["post_parent"]] = $value["post_count"];
}
$data = execute_this_query("SELECT `category_id`,`category_parent` FROM `categories` WHERE `category_parent` <> 0");
foreach ($data as $value) {
$associative_array[$value["category_parent"]] += $associative_array[$value["category_id"]];
}
//Your final associative array has the exact category_id and net post count as you want.
First you can populate an associative array for the quantity in each category like: category_id => post_qty.
After that, you can use the function bellow to calculate for every category and its children:
function count_posts($category_id, $cache) {
$sum = 0;
$sql = "SELECT * FROM `tb_categories` WHERE `category_parent` = " . $category_id;
$rows = $this->get_by_sql($sql);
foreach ($rows as $child) {
$sum += count_posts($child->category_id, $cache);
}
$sum += $cache[$category_id];
return $sum;
}

MySQL Recursive Get Right Nodes PHP

I have a table containing user id with left node and right node (Both are which user id). I am trying to create a function to count all the right nodes and total nodes of a particular user id in PHP.
The Code I have written till now which is always returning 0 :
function allcount($id) //Function to calculate all children count
{
$sqlz = "SELECT * FROM user_transaction_details WHERE user_id = '$id'";
$execsql = mysqli_query($conn,$sqlz);
$array = mysqli_fetch_array($execsql);
echo $sqlz;
(array_count_values($array));
$count = 0;
echo $array['l_node'];
if(!empty($array['l_node']))
{
$count += allcount($array['l_node']) +1;
}
if(!empty($array['r_node']))
{
$count += allcount($array['r_node']) +1;
}
return $count;
}
Here is the table structure :
Table Structure
Can anybody please help.
You need to pass $conn as an argument or declare it as global inside the function, because otherwise it is not available inside the function.

How to fetch query where sub/child create new UL

I am trying to make un-order list for parent child categories where if there is any child category than it will create another un-order list ( like indented text) so user can understand properly.
I have fetch sql but with foreach I don't understand how to set so where child category only will display under parent category by creating another un-order list under the parent category.
Here is my code
$query_cat = "SELECT * FROM ^categories";
$query = qa_db_query_sub($query_cat);
$catsid = qa_db_read_all_assoc($query);
echo '<UL>';
foreach ($catsid as $catid){
echo '<LI>'. $catid['title'].' '. $catid['categoryid'].'</LI>';
}
echo '</UL>';
So final result would be
First Category
Sub Category1
Second Category
EDIT:
After modified code with #vlcekmi3 answer https://stackoverflow.com/a/13451136/1053190 I am getting this result
Now how to exclude subcategory from parent list?
There's no really easy solution for this with your design. The most effective way would be to add column like order_in_list (and maybe depth_in_list).
They would be pre calculated in loop (pseudocode):
START TRANSACTION
UPDATE t1 SET order_in_list = 0 // Restart whole loop
$ids = array(0);
while $id = array_shift($ids){
$record = SELECT * FROM t1 WHERE id = $id // Get id details, order_in_list is important
$children = SELECT * FROM t1 WHERE parent_id = $id // get list of all childs
// If it's root element, start indexing from 0
$root_order = ($record ? $record->order_in_list : 1)
$child_no = count($children) // How many child will be adding
// No children, nothing to do:
if $child_no < 1{
continue;
}
append_to_array($ids, $children) // Store ids to process
// Shift all later records, we'll be creating gap in order_in_list 1,2,3,4,5
// To 1,2,5,6,7 to insert items on places 3,4
UPDATE t1 SET order_in_list = (order_in_list + $child_no)
WHERE order_in_list > $record->order_in_list
// Okay, set IDs for direct children
foreach( $children as $child){
UPDATE t1 SET order_in_list = $root_order, depth_in_list = $record->depth_in_list+1
WHERE id = $child->id
$root_order++;
}
}
COMMIT
This way you'll get records like:
First category, 1, 1
Second category 3, 1
Sub category, 2, 2
Which you could display with simple loop:
$last_depth = 0;
foreach( (SELECT * FROM t1 ORDER by `order_in_list`) as $row){
if( $last_detph > $row['depth_in_list'])){
// Close level </ul>
} else if($last_detph < $row['depth_in_list']){
// Opening level <ul>
} else {
// The same depth
}
$last_depth = $row['depth_in_list'];
}
Without modifying database
It would be probably most effective to build two arrays containing root elements and all elements:
$root_elements = array();
$all_elements = array();
foreach( (SELECT * FROM t1) as $row){
// Store details into all_elements, note that entry may have already be created when
// processing child node
if( isset( $all_elements[$row['id']])){
// set details
} else {
$all_elements[$row['id']] = $row;
$all_elements[$row['id']]['children'] = array(); // Array of child elements
}
if( $row['parent_id'] == NULL){
$all_elements[] = $row['id']; // Add row element
} else {
if( isset( $all_elements[ $row[ 'parent_id']])){
$all_elements[ $row[ 'parent_id']]['children'][] = $row['id'];
} else {
// Create new record:
$all_elements[ $row[ 'parent_id']] = array();
$all_elements[ $row[ 'parent_id']]['children'] = array($row['id']);
}
}
}
And then write it as:
foreach( $root_elements as $element_id){
write_recursive( $all_elements[ $element_id]);
}
// And display
function write_recursive( $element)
{
echo '<ul>...';
if( count( $element['children'])){
foreach( $element['children'] as $child){
write_recursive( $all_elements[ $child]);
}
}
echo '</ul>';
}
You better create class for that (to replace using global variables), but you should have a solid way to do this. Anyway try avoid using this with large number of records (I wouldn't go past 2000-5000 menu entries), try to at least cache it.
Note: solutions are oriented towards minimal number of requests on database when displaying list.
you can use complicated query or something like this
foreach ($catsid as $catid) {
...
$subquery_cat = "SELECT * FROM ^categories WHERE parentid='".$catid['categoryid']."'";
$query = qa_db_query_sub($subquery_cat);
$subcatsid = qa_db_read_all_assoc($query);
// wrap into html
...
}

How do I extract and display hierarchical data from my database?

I have two tables.
The chapters table has the columns id and name.
The chapters_chapter table has columns id, master_id, and slave_id.
Lets say that the chapters table has 7 records:
id name
1 test01
2 test02
3 test03
4 test04
5 test05
6 test06
7 test07
And in the chapters_chapters table I have these records:
id master_id slave_id
1 1 5
2 1 6
3 6 7
4 7 2
Given that data, how can I extract the hierarchy of that data so that it looks like this?
test01
test05
test06
test07
test02
test03
test04
So this was kind of a pain because of the fact that we had to have the hierarchy stored in the DB. Because of this, each item can have multiple children, and each child can have multiple parents.
This second part means we cannot simply loop through the list once and be done with it. We might have to insert an item in multiple places in the hierarchy. While you probably won't actually structure your data that way, the database schema you've described supports this scenario, so the code must support it too.
Here's a high-level version of the algorithm:
Query both tables
Create a map (array) of a parent (number) to its children (another array)
Create a set of items that are children (array of numbers)
Create a function that displays a single item, indenting it to the desired depth.
If that item has children, this function increases the depth by one, and calls itself recursively
Loop through all items that aren't children (root items).
Call the function for each of those items, with a desired depth of 0 (no indent).
Here's two hours work. Enjoy :)
Note that I stuck it within a <pre> block, so you might have to mess with how the indentation is done (output something other than two spaces, mess with the style of the divs, etc).
<?php
$con = mysql_connect("localhost", "test_user", "your_password");
if(!$con)
{
die("could not connect to DB: " . mysql_error());
}
mysql_select_db("your_db", $con);
// get chapters
$chapters = array();
$result = mysql_query("SELECT * FROM chapters");
while($row = mysql_fetch_array($result))
{
$id = $row["id"];
$name = $row["name"];
$chapters[$id] = $name;
}
// get chapters_chapters - We'll call it "parent/child" instead of "master/slave"
$parent_child_map = array();
$is_child = array();
$result = mysql_query("SELECT master_id, slave_id FROM chapters_chapters");
while($row = mysql_fetch_array($result))
{
$parent_id = $row["master_id"];
$child_id = $row["slave_id"];
$children = $parent_child_map[$parent_id];
if($children == null)
{
$children = array();
}
$children[] = $child_id;
$parent_child_map[$parent_id] = $children;
$is_child[$child_id] = true;
}
// display item hierarchically
$display_item_and_children = function($id, $name, $depth)
use ($chapters, $parent_child_map, &$display_item_and_children)
{
echo "<div><pre>";
// indent up to depth
for($i = 0; $i < $depth; $i++)
{
echo " ";
}
echo "id: " . $id
. " name: " . $name
. "</pre></div>";
// if there are children, display them recursively
$children = $parent_child_map[$id];
if($children != null)
{
foreach($children as $child_id)
{
$child_name = $chapters[$child_id];
$display_item_and_children($child_id, $child_name, $depth + 1);
}
}
};
// display all top-level items hierarchically
foreach($chapters as $id => $name)
{
// if it is a top-level item, display it
if($is_child[$id] != true)
{
$display_item_and_children($id, $name, 0);
}
}
mysql_close($con);
?>
And here's a screenshot:
The question becomes how complex you want your solution to be. I'd do it with the following pseudo code.
SELECT all the chapters
SELECT all the *chapters_chapters*
loop over the chapters to create an array chapter objects
loop over the `chapters_chapters* and create the relationships using the chapter objects
Essentially you're creating a link-list.

Categories