I have a MySQL table with categories whic look like:
categoryID | categoryName | parentID
------------------------------------
1 | books | NULL
------------------------------------
2 | newspapers | NULL
------------------------------------
3 | sci-fi | 1
------------------------------------
4 | robot stories | 3
-------------------------------------
etc.
I need to build a category tree with recursion when I have only the ID of 'robot stories' and it has to look like:
books -> sci-fi -> robot stories
Any advise will be helpful!
If the tree is not too big, I would just load the whole thing in memory (you can cache it too):
$tree = [];
$stmt = $db->query("SELECT * FROM categories");
while (($row = $stmt->fetch(PDO::FETCH_ASSOC)) !== false) {
$tree[$row['categoryID']] = $row;
}
$currentCategoryId = 4;
$hierarchy = [];
// build hierarchy, working from leaf to root
while (isset($tree[$currentCategoryId])) {
array_unshift($hierarchy, $tree[$currentCategoryId]);
$currentCategoryId = $tree[$currentCategoryId]['parentId'];
}
print_r($hierarchy);
Related
I want to make some like threaded data, so the data is showing with tree-view, but there is a problem, the data is not showing correctly, can you give me some suggest?
Here my table
+-------------+--------------------+----------------------------------+
| division_id | division_parent_id | division_name |
+-------------+--------------------+----------------------------------+
| 1 | 0 | division one |
+-------------+--------------------+----------------------------------+
| 2 | 1 | division one child one |
+-------------+--------------------+----------------------------------+
| 3 | 1 | division one child two |
+-------------+--------------------+----------------------------------+
| 4 | 2 | division grandchild one-one |
+-------------+--------------------+----------------------------------+
| 5 | 2 | division grandchild one-two |
+-------------+--------------------+----------------------------------+
| 6 | 3 | division grandchild two-one |
+-------------+--------------------+----------------------------------+
| 7 | 3 | division grandchild two-two |
+-------------+--------------------+----------------------------------+
| 8 | 4 | division greatgradchild one-one |
+-------------+--------------------+----------------------------------+
| 9 | 4 | division greatgrandchild one-two |
+-------------+--------------------+----------------------------------+
and my code
function getThreaded($row){
$result = "<ul>";
$result .= "<li>".$row['division_name']."</li>";
$q = $this->db->get_where('tb_m_division', array('division_parent_id' => $row['division_id']));
if($q->num_rows() > 0 )
{
foreach($q->result_array() as $row)
{
$result .= $this->getThreaded($row);
}
}
$result .="</ul>";
return $result;
}
function prepare(){
$q = $this->db->get_where('tb_m_division', array('division_parent_id' => '0'));
foreach($q->result_array() as $row)
{
$result = $this->getThreaded($row);
}
// $this->load->view('header');
// $this->load->view('v_tree', $data);
// $this->load->view('footer');
echo $result;
}
the data only displaying the last inputed, if I add the new data then the another data is not displaying.
You need a recursive function. Something like:
function getDivisions($parent_id = 0)
{
$result = '';
$this->db->where('division_parent_id', $parent_id);
$query = $this->db->get('tb_m_division', 0);
if ($query->num_rows() > 0)
{
$rows = $result_array();
$result = "<ul>";
foreach($rows as $row)
{
$result .= "<li>".$row['division_name']."</li>";
// get children
$result .= $this->getDivision($row['division_id']);
$result .= "</ul>";
}
}
return $result;
}
I have not tested that code, so it might be buggy, but the idea is that the function calls itself to get children of each row.
Hope that helps,
Paul
PS If you need to get different classes onto the children just send a level counter through to the function too, so level=1 is top level, level=2 is children, level=3 is grandchildren. Then alter the li line depending on the level set.
below is my table data
+-------------+-----------+----------------+
| customer_id | parent_id | node_direction |
+-------------+-----------+----------------+
| 1 | 0 | T |
| 2 | 1 | L |
| 3 | 1 | R |
| 4 | 2 | L |
| 5 | 2 | R |
| 6 | 4 | L |
+-------------+-----------+----------------+
Which represents the following tree
1
|
---------
| |
2 3
|
-------
| |
4 5
|
-----
|
6
I need to find the position for insertion by parent id
For Example:
1) if parent id is 1 then insert position will be root-3 position-L
2) if parent_id is 2 then insert position will be root-4 position-R
3) if parent_id is 3 then insert position will be root-3 position-L
The thing is it need to follow the binary structure
I also need to have count of sub nodes by parent node for example:
1 - 5
2 - 3
3 - 0
4 - 1
5 - 0
I need to accomplish this in php and mysql.
Can anyone suggest to me the easiest way to achieve this?
function getNodeInsertPostionByParentId($parentId){
$position = array('status'=>false);
$parents = array();
foreach($parentId as $parent){
$qry = "select customer_id,node_direction from mlm_nodes where parent_id =".$parent." order by node_direction";
$rst = mysql_query($qry);
$count = mysql_num_rows($rst);
if($count==2){
while($row = mysql_fetch_assoc($rst)){
$parents[$parent][] = $row['customer_id'];
}
}elseif($count==1){
$position['status'] = true;
$position['parentId'] = $parent;
$position['node'] = 'R';
//echo '<pre>1';print_r($position);echo '</pre>';
return $position;
}else{
$position['status'] = true;
$position['parentId'] = $parent;
$position['node'] = 'L';
//echo '<pre>2';print_r($position);echo '</pre>';
return $position;
}
}
return $this->searchByParents($parents);
}
function searchByParents($parents){
foreach($parents as $parent){
return $this->getNodeInsertPostionByParentId($parent);
}
}
echo '<pre>';print_r($this->getNodeInsertPostionByParentId(array('4')));die;
This works as expected for finding node position by parent id
I was reading this article, http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/.
I wanted to give a simple example and then ask you how do I get the desired result? So here is the example:
+---------+-----------------------------+
| product_id | product_name |
+---------+-----------------------------+
| 1 | Example Product |
+---------+-----------------------------+
+---------+-----------------------------+
| product_id | category_id |
+---------+-----------------------------+
| 1 | 2 |
| 1 | 4 |
+---------+-----------------------------+
+-------------+--------------------+------+------+
| category_id | name | lft | rgt |
+-------------+--------------------+------+------+
| 1 | Electronics | 1 | 8 |
| 2 | Televisions | 2 | 3 |
| 3 | Portable Electronics | 4 | 7 |
| 4 | CD Players | 5 | 6 |
+-------------+--------------------+------+------+
I want to be able to display the following result in HTML after querying and then manipulating the data in PHP:
"Example Product" Categories:
Electronics
Televisions
Portable Electronics
CD Players
Can you help walk me through the query and manipulation in PHP to achieve this result?
Some specifics to think about:
Notice how both categories are under Electronics, but "Electronics" appears only once here, displaying each of the subcategories it belongs to below
The result should eventually be a PHP multi-dimensional array with the category containing an array of sub-categories and each sub-category containing an array of sub-subcategories if they exist.
I imagine printing the depth will be very important for constructing the right tree in HTML.
I thought this was a nice challenge .. here's my solution:
Basically: read a node, then all following nodes with a rgt smaller than your rgt are your children, do this recursively.
I've used a peek/consume to read from mysql like you normally would.
The script will break or loop if the query gives no results, or if the data-set is broken.
class NestedNodeReader {
private $mysql_result;
private $peeked = false;
private $last_peek;
public function __construct($mysql_result) {
$this->mysql_result = $mysql_result;
}
public function getTree() {
$root = $this->consume();
$root["children"] = $this->getSubTree($root["rgt"]);
return $root;
}
private function getSubTree($stop_at) {
$nodes = array();
$node = $this->peek();
while ($node["rgt"] < $stop_at) {
$node = $this->consume();
$node["children"] = $this->getSubTree($node["rgt"]);
$nodes[] = $node;
$node = $this->peek();
if (false === $node) {
break;
}
}
return $nodes;
}
private function peek() {
if (false === $this->peeked) {
$this->peeked = true;
$this->last_peek = mysql_fetch_assoc($this->mysql_result);
}
return $this->last_peek;
}
private function consume() {
if (false === $this->peeked) {
return mysql_fetch_assoc($this->mysql_result);
} else {
$this->peeked = false;
return $this->last_peek;
}
}
}
$query = "SELECT node.name, node.lft, node.rgt
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND parent.name = 'ELECTRONICS'
ORDER BY node.lft;"
$mysql_result = mysql_query($query);
$nnr = new NestedNodeReader($mysql_result);
print_r($nnr->getTree());
I have to build a tree that will contain about 300 nodes inside it. The tree has no depth limitations. So it can have 3 or 15 levels. Each node can have an unlimited number of children.
The priority is to get a complete tree / subtree the faster as possible, but I also need to add nodes or move nodes sometimes but not that often.
I want to know the best way to store the tree in the database and the best way to retrieve the data, if possible, in php.
You can use a Nested Set Model as it yields very efficient queries. Check out Managing Hierarchical Data in MySQL and read the section called Nested Set Model.
If you're using an ORM like Doctrine, it includes nested set capabilities.
It can be difficult for some to grasp the nested set concepts of left and right. I have found that using those numbers as an analogy for the line numbers of open/close tags in an XML document, folks find it easier to grasp.
For instance, take the data example from the MySQL link above:
+-------------+----------------------+-----+-----+
| category_id | name | lft | rgt |
+-------------+----------------------+-----+-----+
| 1 | ELECTRONICS | 1 | 20 |
| 2 | TELEVISIONS | 2 | 9 |
| 3 | TUBE | 3 | 4 |
| 4 | LCD | 5 | 6 |
| 5 | PLASMA | 7 | 8 |
| 6 | PORTABLE ELECTRONICS | 10 | 19 |
| 7 | MP3 PLAYERS | 11 | 14 |
| 8 | FLASH | 12 | 13 |
| 9 | CD PLAYERS | 15 | 16 |
| 10 | 2 WAY RADIOS | 17 | 18 |
+-------------+----------------------+-----+-----+
If you take the lft, rgt fields and use them as line numbers for an XML document, you get:
1. <electronics>
2. <televisions>
3. <tube>
4. </tube>
5. <lcd>
6. </lcd>
7. <plasma>
8. </plasma>
9. </televisions>
10. <portable electronics>
11. <mp3 players>
12. <flash>
13. </flash>
14. </mp3 players>
15. <cd players>
16. </cd players>
17. <2 way radios>
18. </2 way radios>
19. </portable electronics>
20. </electronics>
Seeing it this way can make it much easier for some to visualize the resulting nested set hierarchy. It also makes it clearer why this approach improves efficiency as it makes it possible to select entire nodes without the need for multiple queries or joins.
This is great article about it: Managing Hierarchical Data in MySQL. I used for a long time.
If you have some mathematical capabilities, you can really understand why it is so great!
<?php
$host = "localhost";
//Database user name.
$login = "root";
//Database Password.
$dbpass = "";
$dbname = "abc";
$PDO = new PDO("mysql:host=localhost;dbname=$dbname", "$login", "$dbpass");
$rows = array();
$sql = 'SELECT id, parent_id, name FROM employee';
$query = $PDO->prepare($sql);
$query->execute();
$rows = array();
if (!$query)
{
$error = 'Error fetching page structure, for nav menu generation.';
exit();
}
while($row = $query->fetch(PDO::FETCH_ASSOC)){
if( strcasecmp($row['parent_id'],'null') === 0 || empty($row['parent_id']) ) {
$row['parent_id'] = null;
}
$rows[] = $row;
}
// covert raw result set to tree
$menu = convertAdjacencyListToTree(null,$rows,'id','parent_id','links');
// echo '<pre>',print_r($menu),'</pre>';
// display menu
echo themeMenu($menu,1);
/*
* ------------------------------------------------------------------------------------
* Utility functions
* ------------------------------------------------------------------------------------
*/
/*
* Convert adjacency list to hierarchical tree
*
* #param value of root level parent most likely null or 0
* #param array result
* #param str name of primary key column
* #param str name of parent_id column - most likely parent_id
* #param str name of index that children will reside ie. children, etc
* #return array tree
*/
function convertAdjacencyListToTree($intParentId,&$arrRows,$strIdField,$strParentsIdField,$strNameResolution) {
$arrChildren = array();
for($i=0;$i<count($arrRows);$i++) {
if($intParentId === $arrRows[$i][$strParentsIdField]) {
$arrChildren = array_merge($arrChildren,array_splice($arrRows,$i--,1));
}
}
$intChildren = count($arrChildren);
if($intChildren != 0) {
for($i=0;$i<$intChildren;$i++) {
$arrChildren[$i][$strNameResolution] = convertAdjacencyListToTree($arrChildren[$i][$strIdField],$arrRows,$strIdField,$strParentsIdField,$strNameResolution);
}
}
return $arrChildren;
}
/*
* Theme menu
*
* #param array menu
* #param runner (depth)
* #return str themed menu
*/
function themeMenu($menu,$runner) {
$out = '';
if(empty($menu)) {
return $out;
}
$out.='<ul>';
foreach($menu as $link) {
$out.= sprintf(
'<li class="depth-%u">%s%s</li>'
,$runner
,$link['name']
,themeMenu($link['links'],($runner+1))
);
}
$out.='</ul>';
return $out;
}
?>
I have a table that like this.
+-----------+-----------+-----------+-----------+-----------+-----------+
|id | parent_id | name | order | status | date_add |
+-----------+-----------+-----------+-----------+-----------+-----------+
|1 | 0 | shoes | 1 | 1 | 2011-04-02|
+-----------+-----------+-----------+-----------+-----------+-----------+
|2 | 1 | male | 2 | 1 | 2011-04-02|
+-----------+-----------+-----------+-----------+-----------+-----------+
|3 | 1 | female | 3 | 1 | 2011-04-02|
+-----------+-----------+-----------+-----------+-----------+-----------+
|4 | 3 | red shoes | 4 | 1 | 2011-04-02|
+-----------+-----------+-----------+-----------+-----------+-----------+
I want to select only the leaf children, with their path.
I want to come to the result as follows:
+------+-------------------------------------+
| 2 | shoes/male |
+------+-------------------------------------+
| 4 | shoes/female/red shoes |
+------+-------------------------------------+
if this does not only sql can also be php + sql
Please help me.
Very simple solution to print out id and path to all last child nodes using PHP as I am not aware of a way of doing this within MySQL. Hope this helps!
function getChildren($parent= "", $x = 0) {
$sql = "SELECT id, name FROM recurr WHERE parentId = $x";
$rs = mysql_query($sql);
//echo "Name: $parent has ". mysql_num_rows($rs)." children<br/>";
while ($obj = mysql_fetch_object($rs)) {
if (hasChildren($obj->id)) {
getChildren($parent."/".$obj->name, $obj->id);
} else {
echo $obj->id .", ".$parent."/".$obj->name."<br/>";
}
}
}
function hasChildren($x) {
$sql = "SELECT * FROM recurr WHERE parentId = $x";
$rs = mysql_query($sql);
if (mysql_num_rows($rs) > 0) {
return true;
} else {
return false;
}
}
To run just call:
getChildren();
I highly recommend implementing nested set model for hierarchical MySQL data. Here is the link for more info: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/. In the link, you will also find the answer to your question (under "The Adjacency List Model", "Finding all the Leaf Nodes" section)