I have this mysql table "categories":
id category parent
1 category1 NULL
2 subcategory1 1
3 category2 NULL
4 subcategory2 2
and I want to get this result:
category1
subcategory1
subcategory2
category2
to get this result I use this code, but is very slow:
foreach($db->query("SELECT * FROM categories WHERE parent IS NULL") as $parent)
{
$t=0;
categories($db,$t,$parent['id'],$parent['category']);
}
function categories($db,$t,$id,$category)
{
echo"<option value=".$id.">";//
for($i=0;$i<$t;$i++) {echo" "; $GLOBALS['cat'].=" ";}
echo $category."</option>";//" ".$id.
$GLOBALS['cat'].=$category."<br>";
$t++;
if($db->query("SELECT * FROM categories WHERE parent=".$id)->rowCount()>0)
{
foreach($db->query("SELECT * FROM categories WHERE parent=".$id) as $child)
categories($db,$t,$child['id'],$child['category']);
}
}
Do you have a faster solution?
Thanks
If you know the depth of your tree (maximum or desired), you can have it all in SQL, flattened to a row representing path:
SELECT c.category, c1.category AS sub1, c2.category AS sub2, c3.category AS sub3
FROM categories c
LEFT JOIN
(categories c1
LEFT JOIN
(categories c2
LEFT JOIN categories c3
ON c3.parent = c2.category)
ON c2.parent = c1.category)
ON c1.parent = c.category
WHERE c.parent IS NULL;
Having that, it's not a big deal to present it in UI accordingly.
$sql = "SELECT id, name, parent_id,
(SELECT COUNT(*) FROM categories WHERE parent_id = cat.id) as child_count
FROM categories cat";
$result = db_select_all($sql);
function build_menu($pid, $level = 1) {
global $result;
$filtered = array_filter($result, function($elem) use($pid) {
return $elem['parent_id'] == $pid;
});
if(empty($filtered)) return;
foreach($filtered as $item) {
extract($item);
echo "<div>" . str_repeat("---", $level) . $name;
build_menu($id, $level + 1);
echo "</div>" . PHP_EOL;
}
}
$menu_elements = array_filter($result, function($item) {
return $item['parent_id'] == '';
});
foreach($menu_elements as $menu) {
$name = $menu['name'];
$id = $menu['id'];
echo "<div>" . $name;
build_menu($id);
echo "</div>" . PHP_EOL;
}
Related
Maybe a simple question, but after hours of reading i still didn't find an answer.
I'm trying to get the values of subrow next to those of parent row, without constantly repeating parent row:
For example:
Table 1: ParentId = 1, ParentName = ParentName
Table 2:
ChildId = 1, ParentId = 1, ChildName = ChildName1
ChildId = 2, ParentId = 1, ChildName = ChildName2
The result with JOIN currently is:
ParentId, ParentName, ChildId, ChildName1
ParentId, ParentName, ChildId, ChildName2
But i would like to display the data like:
<div>Div with parent name</div>
<div>Div with ChildName1</div>
<div>Div with ChildName2</div>
<div>Div with parent name2</div>
<div>Div with ChildName1</div>
<div>Div with ChildName2</div>
etc...
Can anyone tell me how to get to this?
Something like
echo$Parent1 foreach child as $child echo$ChildName?
You can sort by parent ID, then check if it has changed. If it has changed output the name of the parent. Something like this:
$query = "query selecting data with join... ORDER BY ParentId";
$result = $mysqli->query($query);
$lastParent = 0;
while ( $row = $result->fetch_assoc($result) )
{
if ( $row['ParentId'] != $lastParent )
{
echo '<div>' . $row['ParentName'] . '</div>';
}
$lastParent = $row['ParentId'];
echo '<div>' . $row['ChildName'] . '</div>';
}
Using that the parent name will only output when the ID changes, and as we're ordering by ID all the children will be grouped by their Parent and output together.
Issue fixed with following code
$result = mysqli_query($link, "SELECT * FROM TestParent a
LEFT JOIN TestChild b ON a.ParentId = b.ParentId
ORDER BY a.ParentId ASC, b.ChildName ASC");
$lastParent = 0;
while($row = mysqli_fetch_assoc($result))
{
if ( $row['ParentId'] != $lastParent ) {
echo '<div>' . $row['ParentName'] . '</div>';
}
$lastParent = $row['ParentId'];
echo '<div>' . $row['ChildName'] . '</div>';
}
I am very new to OOP and haven't been programing in php in years, but essentially what I am trying to do is to display a multilevel category tree in a CRUD application. I have a Category class and a method that will be the one to display the appropriate category based on the parent_id. As of now my other attempts display the whole thing, that as is all categories and subcategories in a select statement. this is my Category class you'll notice I tried a few things, that didn't work.
class Category{
// database connection and table name
private $conn;
private $table_name = "menu";
// object properties
public $id;
public $name;
public $parent_id;
public function __construct($db){
$this->conn = $db;
}
//Builds the category tree recursively fetching data
function build_category_tree(&$output, $preselected, $parent=0, $indent=""){
$query = "SELECT id, name FROM menu WHERE parent_id = " . $parent . "
";
while($r = $query->fetch(PDO::FETCH_ASSOC)){
$selected = ($r["id"] == $preselected) ? "selected=\"selected\"" : "";
$output .= "<option value=\"" . $r["id"] . "\" " . $selected . ">" . $indent . $r["name"] . "</option>";
if($r["id"] != $parent){
build_category_tree($output, $preselected, $r["id"], $indent . " ");
}
}
}
// used by select drop-down list
function read(){
//select all data
$query = "SELECT
id, name
FROM
" . $this->table_name . "";
/*$query = "SELECT t1.name AS lev1, t2.name as lev2, t3.name as lev3, t4.name as lev4, t5.name as lev5\n"
. "FROM menu AS t1\n"
. "LEFT JOIN menu AS t2 ON t2.parent_id = t1.id\n"
. "LEFT JOIN menu AS t3 ON t3.parent_id = t2.id\n"
. "LEFT JOIN menu AS t4 ON t4.parent_id= t3.id\n"
. "LEFT JOIN menu AS t5 ON t5.parent_id= t4.id\n"
. "WHERE t1.parent_id = 0";*/
$stmt = $this->conn->prepare( $query );
$stmt->execute();
return $stmt;
}
function readName(){
/*$query = "SELECT t1.name AS lev1, t2.name as lev2, t3.name as lev3, t4.name as lev4, t5.name as lev5\n"
. "FROM menu AS t1\n"
. "LEFT JOIN menu AS t2 ON t2.parent_id = t1.id\n"
. "LEFT JOIN menu AS t3 ON t3.parent_id = t2.id\n"
. "LEFT JOIN menu AS t4 ON t4.parent_id= t3.id\n"
. "LEFT JOIN menu AS t5 ON t5.parent_id= t4.id\n"
. "WHERE t1.parent_id = ? limit 0,1";*/
$query = "SELECT name FROM " . $this->table_name . " WHERE id = ? ";
$stmt = $this->conn->prepare( $query );
$stmt->bindParam(1, $this->id);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$this->name = $row['name'];
}
}
The next script builds the select tag with the appropriate categories. As I have 5 levels of categories I am trying to simplify the SELECT and make it more manageable.
// read the product categories from the database
include_once 'objects/category.php';
$category = new Category($db);
$stmt = $category->read();
/* //This works fine in a single level that is no subcategories
echo "<select class='form-control' name='category_id'>";
echo "<option>Seleccioná la categoría...</option>";
while ($row_category = $stmt->fetch(PDO::FETCH_ASSOC)){
extract($row_category);
echo "<option value='{$id}'>{$name}</option>";
}*/
// put them in a select drop-down
$category->build_category_tree($locations, 0);
// echo "</select>";
?><select><?php echo $locations ?></select>
<!-- Test other options -->
<select>
<option value="-1">Seleccioná la categoría</option>
<?php echo $locations ?>
I have a MySQL table ( name: item_categories ) having the following schema:
id INT(11) PRIMARY KEY, AUTO INCREMENT
category_name VARCHAR(100) NOT NULL
parent_id INT(11) NOT NULL, Default 0
Sample Data:
id category_name parent_id
=============================
1 Fruit 0
2 Animal 0
3 Furniture 0
4 Apple 1
5 Orange 1
6 Cat 2
7 Dog 2
8 Black Dog 7
9 White Dog 7
If I have to list out the categories with their parent categories, I have to loop through the database table many times.
My question is: How can I simplify the loop task?
Current loop task query:
// 1st query
$sql = "SELECT * FROM item_categories";
$rs = $db->query($sql);
while($row = $db->result($rs)) {
if((int)$row['parent_id'] === 0) {
// it is parent
echo $row['category_name'] . PHP_EOL;
} else {
$category = $row['category_name'];
// it has parent
$sql = "SELECT * FROM item_categories WHERE id=" . $row['parent_id'];
$rs2 = $db->query($sql);
$row2 = $db->result($rs);
$parent_id = (int)$row2['parent_id'];
while($parent_id != 0) {
$sql = "SELECT * FROM item_categories WHERE id=" . $row['parent_id'];
$rs2 = $db->query($sql);
$row2 = $db->result($rs);
$parent_id = (int)$row2['parent_id'];
$category = $row2['category_name'] . ' > ' . $category;
}
echo $category;
}
}
Which will output:
Fruit
Animal
Furniture
Fruit > Apple
Fruit > Orange
Animal > Cat
Animal > Dog
Animal > Dog > Black Dog
Animal > Dog > White Dog
Based on the link i gave you in the comment - try this:
function renderBreadcrumbs($db, $id, $path = null) {
$res = $db->query("SELECT id, parent_id, category_name FROM item_categories WHERE id = " . $id);
$row = $db->result($res);
$path = $row['category_name'] . ' > ' . $path;
if($row['parent_id'])
$path = renderBreadcrumbs($db, $row['parent_id'], $path);
return $path;
}
$res = $db->query("SELECT id, parent_id, category_name FROM item_categories ORDER BY parent_id ASC");
while($row = $db->result($res)) {
$breadcrumb = null;
if(!$row['parent_id'])
$breadcrumb .= $row['category_name'];
else
$breadcrumb .= renderBreadcrumbs($db, $row['parent_id'], $row['category_name']);
echo $breadcrumb . PHP_EOL;
}
My mysql table looks like this:
I need to make 3 level tree, without House_NR.
-Country
|--City 1
| |--Street 1
| |--Street 2
|
|--City 2
|--City 3
| |--Sreet 1
| |--Steet 2
In PHP I make this sql query:
SELECT
l.Country_ID,
c1.Name AS Country_Name,
l.City_ID,
c2.Name AS City_Name,
l.Street_ID,
s.Name AS Street_Name,
l.House_NR
FROM Location l
JOIN Country c1 ON c1.id = l.Country_ID
JOIN City c2 ON c2.id = l.City_ID
JOIN Street s ON s.id = l.Street_ID
And I have this function:
function getTree($data) {
$tree = prepareTree($data);
drawTree($tree);
}
function prepareTree($data) {
$nodeList = array();
$tree = array();
foreach ($data as $key) {
$tree[$key["Country_ID"]]["name"] = $key["Country_Name"];
$tree[$key["Country_ID"]]["id"] = $key["Country_ID"];
$tree[$key["Country_ID"]]["level"] = 1;
$tree[$key["Country_ID"]]["children"][$key["City_ID"]]["name"] = $key["City_Name"];
$tree[$key["Country_ID"]]["children"][$key["City_ID"]]["id"] = $key["City_ID"];
$tree[$key["Country_ID"]]["children"][$key["City_ID"]]["level"] = 2;
$tree[$key["Country_ID"]]["children"][$key["City_ID"]]["children"][$key["Street_ID"]]["name"] = $key["Street_Name"];
$tree[$key["Country_ID"]]["children"][$key["City_ID"]]["children"][$key["Street_ID"]]["id"] = $key["Street_ID"];
$tree[$key["Country_ID"]]["children"][$key["City_ID"]]["children"][$key["Street_ID"]]["level"] = 3;
}
return $tree;
}
function drawTree($data, $sub = false) {
echo ($sub) ? "<ul class=\"tree-listCat\">" : "<ul class=\"tree-list\">";
foreach($data as $key) {
if ($key["children"] != null) {
echo "<li class=\"tree-itemCat\"><span class=\"tree-itemSel\">" . $key["name"] . "</span></li>";
drawTree($key["children"], true);
} else
echo "<li class=\"tree-item\"><span class=\"tree-itemSel\">" . $key["name"] . "</span></li>";
}
echo "</ul>";
}
This function works fine, but there some problem in function where function draw a html version of tree now I have this: action=search&type=tree&level=" . $key["level"] . "&id=" . $key["id"], but I need to have something like this:
If only country without child then action=search&type=tree&country=id
If only country and city without child street, then action=search&type=tree&country=id&city=id
If all arguments country, city and steet, then action=search&type=tree&country=id&city=id&street_id=id
And also function prepare tree looks bad (In my mind). In few words I need make a tree from Location database.
I am working in php and mysql, I have a table category, I need to display subcategoies under parent categories. Like
cat1
-> Subcat1
-> Subcat11
-> Subcat2
--> Subcat21
--> Subcat211
cat2
-> Subcat2
-> Subcat21
-->Subcat22
-> Subcat3
--> Subcat31
--> Subcat311
My Table structure is like :
CREATE TABLE IF NOT EXISTS `category` (
`cat_id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'This is primary key of the table',
`name` varchar(255) DEFAULT NULL,
`parent_cat_id` bigint(11) NOT NULL,
PRIMARY KEY (`cat_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;
can you please help me out, how can execute my query.
example:
<?php
class RightMenu {
public $menu;
public $patch = '';
public function getMenu($parent = 0) {
$DB = new DataBase();
$result = $DB->exec("SELECT * FROM cont_sections WHERE parentid = $parent AND status = 1 ORDER BY name;");
if(mysql_num_rows($result) > 0) {
echo '<ul>';
while($row = mysql_fetch_assoc($result)) {
if($row['parentid'] == 0){
$this->patch = '';
}
$this->patch .= '/' . $row["translit"];
echo '<li>' . $row["name"] . '';
echo $this->getMenu($row["id"]);
echo '</li>';
}
echo '</ul>';
}
mysql_free_result($result);
unset($DB);
}
}
?>
use SELF JOIN
SELECT p.cat_id, p.name, c.cat_id, c.name
FROM tbl_category p
INNER JOIN tbl_category c ON p.cat_id = c.parent_cat_id
Then manipulate the resultant array with PHP to display in tabular form
You need to write a recursive function to display Category & sub-cat
getAllSubCats(0);
function getAllSubCats($cat, $depth = NULL)
{
$this_cat = mysql_fetch_object(mysql_query("select * from categories where id= '$cat'"));
$indent = str_repeat(" ", $depth);
echo $indent . $this_cat->name . "<br />";
$result = mysql_query("select * from categories where parent_cat_id = '$cat'");
while($cat = mysql_fetch_object($result))
{
getAllSubCats($cat->id, $depth + 1);
}
}
Or use following query If it is one 2 one relation
SELECT cat_id, name, (SELECT name FROM categories WHERE cat_id = a.parent_id) parent_name FROM categories a