Building Multi Level Menu Dynamically with PDO in PHP - php

I have this data in my database:
id nameOfPerson parent
3 John NULL
4 Michel 3
5 Husam 4
6 Khalaf 5
7 Mark 5
----------------------------
and i want to display it in list like this
John
Michel
Husam
Khalaf
Mark
but in my code he just displayed
John
Michel
How i want to Displeyed all data according to parent like above list ?
this is my function whats the wrongs ?
public function familyTree(){
$query = "SELECT id, nameOfPerson, parent FROM person WHERE parent is null";
$statment = $this->db->prepare($query);
$statment->execute();
echo '<ul id ="family">';
while($family = $statment->fetch(PDO::FETCH_OBJ)){
echo '<li>'. $family->nameOfPerson;
$query1 = "SELECT id, nameOfPerson, parent FROM person WHERE parent = :id";
$statment1 = $this->db->prepare($query1);
$statment1->bindValue('id', $family->id);
$statment1->execute();
if($statment1->rowCount() > 0){
echo '<ul>';
while($family2 = $statment1->fetch(PDO::FETCH_OBJ)){
echo '<li>' . $family2->nameOfPerson . '</li>';
}
echo '</ul>';
}
echo '</li>';
}
echo '</ul>';
}

ASSUMPTIONS
Every child has only 1 parent.
CODE
First, I retrieve all data from DB table as it is not efficient to retrieve data individually for each parent increasing DB calls and affecting response time.
Build an associative array of parent and children where every parent has 2 details of each child - nameOfPerson and id.
Iterate over this array in a recursive way and keep building the list for each child and ultimately return them.
Refer SQL Fiddle for table rows.
<?php
$pdo = new PDO("mysql:dbname=newdummy;host=localhost","root","");
$query = $pdo->query("Select * from hierarchy");
$family_tree = [];
$root_parent = -1;
$root_parent_name = "";
function makeTree($query,&$family_tree,&$root_parent,&$root_parent_name){
while($row = $query->fetch(PDO::FETCH_ASSOC)){
if(is_null($row['parent'])){
$root_parent = $row['id'];
$root_parent_name = $row['nameOfPerson'];
}else{
if(!isset($family_tree[$row['parent']])){
$family_tree[$row['parent']] = [];
}
$family_tree[$row['parent']][] = array($row['nameOfPerson'],$row['id']);
}
}
}
function buildList($family_tree,$parent){
$list = "<ul>";
foreach($family_tree[$parent] as $each_child){
$list .= "<li>" . $each_child[0];
if(isset($family_tree[$each_child[1]])){
$list .= buildList($family_tree,$each_child[1]);
}
$list .= "</li>";
}
$list .= "</ul>";
return $list;
}
makeTree($query,$family_tree,$root_parent,$root_parent_name);
echo "<ul>";
echo "<li>$root_parent_name";
echo buildList($family_tree,$root_parent);
echo "</li>";
echo "</ul>";
OUTPUT
<ul>
<li>John
<ul>
<li>Michel
<ul>
<li>
Husam
<ul>
<li>khalaf</li>
<li>Mark</li>
</ul>
</li>
</ul>
</li>
<li>Tross</li>
<li>David</li>
</ul>
</li>
</ul>

Related

PHP ForEach Loop inside while Returning Duplicate Rows

I am trying to create a Nested Checkbox List in PHP. For this I have written Below Code with Multiple Nested foreach loop inside while
<?php
$sql_concern="SELECT * FROM sister_concern where Base_user='$User_id' And Status='Active'";
$result_concern = mysqli_query($link,$sql_concern);
if(mysqli_num_rows($result_concern) > 0){
echo '<ul>';
while($row = mysqli_fetch_array($result_concern)){
$sisid=$row['Id'];
$name=$row['Name'];
$namearray[]=$name;
foreach($namearray as $data){
echo '<li style="list-style-type:none;"><input type="checkbox" name="sister_concern" class="chkMainConcern" id="chkMainConcern'.$sisid.'" value="'.$sisid.'" > '.$name.' </li>';
//fetch module Name for each concern
$sql_module="SELECT * FROM Module where Status='Active'";
$result_module = mysqli_query($link,$sql_module);
//this one coming duplicate
echo '<ul class="modulename'.$sisid.'" style="display:none;">';
while($rowmodule = mysqli_fetch_array($result_module)){
$modid=$rowmodule['Id'];
$modname=$rowmodule['Name'];
$modearray[]=$modname;
foreach($modearray as $val){
//module name
echo '<li style="list-style-type:none;"><input type="checkbox" value="'.$modid.'"> '.$rowmodule['Name'].' </li>';
//fetch module permission for each module
$sql_modhooks="SELECT * FROM Module_hooks where Status='Active' and ModuleID='$modid'";
$result_modhooks = mysqli_query($link,$sql_modhooks);
echo '<ul class="modulehooks'.$modid.'" style="display:none;">';
while($row_modhooks = mysqli_fetch_array($result_modhooks)){
echo '<li style="list-style-type:none;"><input type="checkbox" value="'.$modid.'"> '.$row_modhooks['display_txt'].' </li>';
}
echo '</ul>';
}
}
echo '</ul>';
}
}
echo '</ul>';
}else{
echo 'No Concern Added Yet! Pls Add a Concer First';
}
?>
It is giving me Output like this https://imgur.com/a/xlTMB0l, Result is coming as expected. Only problem value from first foreach loop duplicating as growing. what's wrong in the code
$namearray[]=$name;
This appends to the existing array. Do unset($namearray) before this statement and it should remove your duplicates I feel.

How to convert resultset to nested unordered list and hide sublist items with a value of 0?

I am trying to display the below table value in list and sub-list.
and here is my for loop to display
$sql ="SELECT *FROM objectives";
$result = $conn->query($sql);
$categories = array();
foreach ($result as $result) {
$category = $result['content'];
$categories[$category][] = $result['sub_content'];
}
?>
<ul>
<?php foreach ($categories as $category => $subcategories): ?>
<li>
<?php echo $category; ?>
<ul>
<?php foreach ($subcategories as $subcategory):?>
<li><?php echo $subcategory; ?></li>
<?php endforeach; ?>
</ul>
</li>
<?php endforeach; ?>
</ul>
The data is displayed in list and with sub list. I don't want to display the 0 value in the sub-list.
Everything is fine except the display of 0 in sub-list. Please advise.
try this if you just don't want to display 0
<?php echo ($subcategory != '0')? '<li>'.$test.'</li>' : ''; ?>
and if you don't want to store in array then put this if condition
foreach ($result as $result) {
$category = $result['content'];
if($result['sub_content'] != '0'){
$categories[$category][] = $result['sub_content'];
}
}
Simply implementing echo ($subcategory != '0')? '<li>'.$test.'</li>' : ''; will result in needless markup in the dom. Specifically, you will have empty <ul></ul> tags as nested lists where only a single row containing $subcategory is 0. (Demonstration) These extra bits of markup may cause funky side-effects when css/styling is applied.
Further refinements are advisable as a matter of best practice:
When querying the database, only SELECT the columns that you specifically require for your task.
Add stability to your process by using an ORDER BY clause that will group the rows by content and possibly sort sub_content
Never use more loops than necessary. This task can be (and therefore, theoretically, should be) performed in a single loop.
Recommended Code: (Demo)
$result = $conn->query("SELECT content, sub_content FROM objectives");
$category = null;
$output = '';
foreach ($result as $row) {
if ($category !== $row['content']) { // new parent
if ($category !== null) { // not first iteration
$output .= "<li>$category"; // print parent
if ($sublist) {
$output .= "<ul>$sublist</ul>"; // print all children
}
$output .= "</li>";
}
$category = $row['content']; // overwrite $category
$sublist = ''; // reset sublist
}
if ($row['sub_content'] !== '0'){ // filter row
$sublist .= "<li>{$row['sub_content']}</li>";
}
}
if ($result) { // in case the resultset is empty
echo "<ul>";
echo $output; // print stored markup
echo "<li>$category"; // print last parent
if ($sublist) {
echo "<ul>$sublist</ul>"; // print all children from last parent
}
echo "</li>";
echo "</ul>";
}
Source Code Output:
<ul>
<li>Demonstrate where to find the following documentation:
<ul>
<li>Operating and Safety Strategy</li>
</ul>
</li>
<li>Explain the different turbine main operating states:
<ul>
<li>Power Production</li>
<li>Idle</li>
<li>Stop</li>
</ul>
</li>
<li>Explain how to recognise the current operating mode on the display of the operating panel</li>
<li>Explain the subsystem operating modes:
<ul>
<li>Stop</li>
<li>Manual</li>
</ul>
</li>
<li>Explain the difference between local and remote point of operation</li>
<li>Explain that only one point of operation can be active at a time</li>
</ul>
Rendered Output: (courtesy of phptester.net)

Menu from database with unlimited sub-menus/items?

I'm trying to build this function that display a menu with sub menus from a database. Something that can show like sub-sub-sub-menus and so on... If that's ever something I need.
In the database i have 2 tables, menu and menuItem.
menu
menuItem
php file
function get_menu($id) {
require(dirname(__FILE__) . '/config.php');
// main base
$menu = " SELECT id, menu_name, menu_data, menu_order
FROM mos_menuItem
WHERE menu_parent_id='$id'
AND menu_sub='0'
ORDER BY menu_order";
$menuResult = mysqli_query($conn, $menu);
echo '<ul>';
while ($menuRow = mysqli_fetch_row($menuResult)) {
// main menu
echo '<li>'. $menuRow[1] .'</li>';
// sub base
$sub = "SELECT id, menu_name, menu_data, menu_order, menu_sub
FROM mos_menuItem
WHERE menu_parent_id='$menuRow[0]'
AND menu_sub>='1'
ORDER BY menu_order";
$subResult = mysqli_query($conn, $sub);
echo '<ul>';
while ($subRow = mysqli_fetch_row($subResult)) {
echo '<li>'. $subRow[1] .'</li>';
}
echo '</ul>';
}
echo '</ul>';
mysqli_close($conn);
}
so I need to loop something inside itself? I'm not sure where to go from here.
Recursion is your best friend here.
I am not sure what purpose is served by the menu_sub field. I assume it tells you at what level it is in the menu hierarchy. If that is the case, then you can safely ignore it (menu_parent_id would suffice) to create this multi-level menu hierarchy.
function getMenus($menu_id, $is_sub=false){
$sql = "SELECT id, menu_name, menu_data, menu_order
FROM mos_menuItem
WHERE menu_parent_id='$menu_id'
ORDER BY menu_order";
$mResult = mysqli_query($conn, $sub);
echo '<ul>';
while ($mRow = mysqli_fetch_row($mResult)) {
echo '<li>'. $mRow[1] .'';
getMenus($mRow[0], true);
echo '</li>';
}
echo '</ul>';
}
This does have one drawback where if you want the hierarchy to start from a parent other than 0 (I assume your root menu_id is 0), that particular menu would not show up in the hierarchy.
So after some trial and error i figure it out. It really helped to look at Sheikh Azad code.
The only difference is in my function i have AND menu_sub='$sub' to make it not going loop the main menu item if the id is the same.. If that make sense.
The function
function mos_get_menu_editor($menuId, $sub) {
require(dirname(__FILE__) . '/config.php');
$item = " SELECT id, menu_name, menu_data, menu_order, menu_sub
FROM mos_menuItem
WHERE menu_parent_id='$menuId'
AND menu_sub='$sub'
ORDER BY menu_order";
$itemResult = mysqli_query($conn, $item);
echo '<ul>';
while ($itemRow = mysqli_fetch_row($itemResult)) {
echo '<li>'. $itemRow[1] .'</li>';
mos_get_menu_editor($itemRow[0], 1);
}
echo '</ul>';
mysqli_close($conn);
}

Iterating values fetched from mysqli

So I am creating a dynamic menu. Where I have stored the main categories of the menu in a separate table and subcategories in another table.
They are stored in this way
Categories Table
id cat_name
1 HOME,
2 PRODUCTS,
3 SOLUTIONS,
4 NEWS & GALLERY,
5 DOWNLOADS,
6 CONTACT
Right Now I am running a query
$sql="SELECT * FROM categories";
$query = mysqli_query($con, $sql);
while($row = mysqli_fetch_assoc($query)) {
$row['cat_name'];
$cat=explode(",",$row['cat_name']);
}
Then wherever needed I am printing the values like this
<?php echo $cat[0]; .. echo $cat[1]; //and so on ?>
It looks like this right now. And it is supposed to look likethis
But the problem with this solution is that I have to define the index of the array to print it.
I am developing the admin panel for this one and I want that if the admin adds a new menu item then it should automatically fetch it and display it.
But with this solution it is not possible.
I am thinking of iterating the values with a for loop but cannot get it right.
Use foreach ($query as $rows)
or
Use for ($i=0; $i < count($query); $i++)
Probably You can try this also
$sql="SELECT * FROM categories";
$query = mysqli_query($con, $sql);
while($row = mysqli_fetch_assoc($query))
{
$row['cat_name'];
$cat=explode(",",$row['cat_name']);
foreach($cat as $catss)
{
$cat = trim($catss);
$categories .= "<category>" . $cat . "</category>\n";
}
}
You can try this way may be you wish like this for dynamic menu:
$sql="SELECT * FROM categories";
$query = mysqli_query($con, $sql);
$menu = '';
$menu .= '<ul>';
while($row = mysqli_fetch_assoc($query))
{
$menu .= '<li>' . $row['cat_name'] . '</li>';
}
$menu .= '</ul>';
echo $menu;
When you are fetching data from table then you can directly print that value using field name. if you want to further use that so, you have to add in one array like:
$cat[] = $row['cat_name'];
then you can use this value in that manner using for or while:
foreach ($cat as $value) {
echo $value;
}
echo '<ul>';
foreach ($cat as $val) {
echo "<li>{$val}</li>";
}
echo '</ul>';
Instead of the index. Also this should be faster and readable than for loop.
You can save it in array then later access the values.
$sql="SELECT * FROM categories";
$query = mysqli_query($con, $sql);
$cat = array();
while($row = mysqli_fetch_assoc($query)) {
$cat[]=$row['cat_name']
}
Now:
foreach ($cat as $category) {
$cate_gory = explode("," , $category);
foreach ($cate_gory as $categories) {
echo $categories . "<br>";
}
}
Foreach Loop
foreach($cats as $cat)
{
echo $cat;
}
Basic For Loop
for ($i=0; $i<count($cats); $i++)
{
echo $cats[$i];
}
Simple Use Case
<?php
$cats = 'Stack overflow PHP questions';
$cats = explode(' ',$cats);
$html = '<ul>';
foreach($cats as $cat)
{
$html .= '<li>' . $cat . '</li>';
}
$html .= '</ul>';
echo $html;
?>
Your Code
In this case the while loop you are using will iterate over all the values returned from the sql statement, so there is no need to add more complexity. I would remove the commas from the field names and echo out the html this way.
$sql="SELECT * FROM categories";
$query = mysqli_query($con, $sql);
$html = '<ul>';
while($row = mysqli_fetch_assoc($query)) {
$html .= '<li>' . $row['cat_name'] . '</li>';
}
$html .= '</ul>';
echo $html;
?>

Magento - get a parent category and all sub-sub-categories

I have a single category, that has 2 subcategories. Within each of these categories, are 5 subcategories.
Is there a way to get a list of all of these 10 sub-sub-categories?
Thanks
EDIT:
Something like this:
Main Category
Sub_Cat_1
Cat_1
Cat_2
Cat_3
Sub_Cat_2
Cat_4
Cat_5
Cat_6
Wanting output like:
Cat_1
Cat_2
Cat_3
Cat_4
Cat_5
Cat_6
Thanks
All answers so far load children categories in a loop which is generally bad practice and causes execution of many SQL queries where a single one would suffice.
Performant Single Query Solution:
Let $parentCategory be your Main Category, then this collection will load all subcategories, two levels below:
$subcategoryCollection = Mage::getModel('catalog/category')
->getCollection()
->addFieldToFilter('level', $parentCategory->getLevel() + 2)
->addFieldToFilter('path', ['like' => $parentCategory->getData('path') . '/%']);
The path field contains the category id prefixed with all ancestor ids in the form 1/2/3. Database wise it is a column in catalog_category_entity that has an index, so comparison like this has no performance issues.
Figured it out:
$cat = Mage::getModel('catalog/category')->load(24);
$subcats = $cat->getChildren();
foreach(explode(',',$subcats) as $subCatid)
{
$_category = Mage::getModel('catalog/category')->load($subCatid);
if($_category->getIsActive()) {
$sub_cat = Mage::getModel('catalog/category')->load($_category->getId());
$sub_subcats = $sub_cat->getChildren();
foreach(explode(',',$sub_subcats) as $sub_subCatid)
{
$_sub_category = Mage::getModel('catalog/category')->load($sub_subCatid);
if($_sub_category->getIsActive()) {
echo '<li class="sub_cat">'.$_sub_category->getName().'</li>';
}
}
}
}
Thanks for looking!
I have developed a recursive function to get all children of a category
$categoryObject = Mage::getModel('catalog/category')->load(CATID);
$children = MODEL_OBJECT->getChildCategories($categoryObject);
// IN MODLE FILE
public $_catIds = array();
public function getChildCategories($categoryObject){
$categories = $categoryObject->getChildrenCategories();
foreach ($categories as $catgory){
if($catgory->hasChildren()){
$this->getChildCategories($catgory);
}
$this->_catIds[] = $catgory->getId();
}
return $this->_catIds;
}
Hope this will help you.
$this->getCurrentCategory()->getParentCategory()->getData();
try this
What I do is recursively find the id of the ancestor category.
Once found it, I can print the current category sub categories.
If the current category has no children, than I will just print the father's subcategory.
In this way you can have infinite subcategories and still being able to display a sub category list.
$_helper = Mage::helper("catalog/category");
$rootCat = Mage::app()->getStore()->getRootCategoryId();
$current = Mage::registry('current_category');
show_cat($current,$_helper,$rootCat);
function show_cat($child,$_helper,$rootCat){
if ($child){
//look for anchestor
$parentid = $child->getParentId();
$parent = Mage::getModel("catalog/category")->load($parentid);
if($parentid != $rootCat)
{
//find the anchestor
show_cat($parent,$_helper,$rootCat);
}else{
//is root
$_subcategories = $parent->getChildrenCategories();
if(count($_subcategories)>0){
echo '<ul>';
foreach($_subcategories as $_category){
echo '<li>';
echo ''.$_category->getName().'';
if($child->getId() == $_category->getId()){
$current = Mage::registry('current_category');
if ($current){
//handle current
$_current_subcategories = $current->getChildrenCategories();
if(count($_current_subcategories)>0){
//the current cat has childrens
echo '<ul>';
foreach($_current_subcategories as $_sub_category){
echo '<li>';
echo ''.$_sub_category->getName().'';
echo '</li>';
}
echo '</ul>';
}else{
//the current cat has no childrens
$current_parent = $current->getParentId();
$current_parent = Mage::getModel("catalog/category")->load($current_parent );
$_current_subcategories = $current_parent ->getChildrenCategories();
echo '<ul>';
foreach($_current_subcategories as $_sub_category){
echo '<li>';
echo ''.$_sub_category->getName().'';
echo '</li>';
}
echo '</ul>';
}
}
}
echo '</li>';
}
echo '</ul>';
}
}
}
}

Categories