Get all children items from flat array with nested data structure - php

I have a flat array of categories. All categories have ID, Parent_id and Name.
The root categories have Parent_id equal to null. They have subcategories that might have them as their parents and their might be subsubcategories that have subcategories as parents. (their might be a lot of levels).
I can get all categories with a single query into a flat array. What I need is - If I take a category (or a subcaegory), how can I get a list of all nested categories (subcategories, subsubcategories) that are included in this category? Got stack with that problem :(
Array looks like this:
Array
(
[0] => Array
(
[pk_i_id] => 2
[fk_i_parent_id] =>
[i_expiration_days] => 30
[i_position] => 0
[b_enabled] => 1
[b_price_enabled] => 1
[s_icon] =>
)
[1] => Array
(
[pk_i_id] => 4
[fk_i_parent_id] =>
[i_expiration_days] => 30
[i_position] => 6
[b_enabled] => 1
[b_price_enabled] => 1
[s_icon] =>
)
[2] => Array
(
[pk_i_id] => 12
[fk_i_parent_id] =>
[i_expiration_days] => 60
[i_position] => 11
[b_enabled] => 1
[b_price_enabled] => 1
[s_icon] =>
)
[3] => Array
(
[pk_i_id] => 13
[fk_i_parent_id] => 108
[i_expiration_days] => 30
[i_position] => 0
[b_enabled] => 1
[b_price_enabled] => 1
[s_icon] =>
)

You can use recursion. It will be looks like this:
function outTree(array $tree, $parentId = null) {
echo '<ul>';
foreach ($tree as $row) {
if ($row['fk_i_parent_id'] == $parent_id) {
echo '<li>' . $row['pk_i_id'];
echo outTree($tree, $row['pk_i_id']);
echo '</li>';
}
}
echo '</ul>';
}

Related

Face problem during get unique value from foreach loop

I have faced a problem during get unique value in foreach loop.
Following is my array.
Array
(
[20] => Array
(
[0] => Array
(
[id] => 4
[category_title] => Specialist Range
[parent] => 20
[front_active] => 1
[category_date] => 2019-05-21 04:04:17
)
[1] => Array
(
[id] => 4
[category_title] => Specialist Range
[parent] => 20
[front_active] => 1
[category_date] => 2019-05-21 04:04:17
)
[2] => Array
(
[id] => 4
[category_title] => Specialist Range
[parent] => 20
[front_active] => 1
[category_date] => 2019-05-21 04:04:17
)
[3] => Array
(
[id] => 6
[category_title] => Cater Foil Rolls
[parent] => 20
[front_active] => 1
[category_date] => 2019-05-21 04:04:24
)
)
[21] => Array
(
[8] => Array
(
[id] => 24
[category_title] => Specialist Range
[parent] => 21
[front_active] => 1
[category_date] => 2019-05-21 04:07:59
)
[9] => Array
(
[id] => 24
[category_title] => Specialist Range
[parent] => 21
[front_active] => 1
[category_date] => 2019-05-21 04:07:59
)
)
)
I written following script for getting unique value in loop
$catArray = array();
foreach($catagory_list as $key=>$catagory){
$catArray[$catagory['parent']][$key] = $catagory;
}
ksort($catArray, SORT_NUMERIC);
foreach ($catArray as $key => $value) {
if($key == 20 ){
$catName ='local';
}elseif ($key == 21) {
$catName ='interstate';
}elseif ($key == 22) {
$catName ='wholesale';
}elseif ($key == 23) {
$catName ='tst';
}
//echo $key;
foreach(array_unique($value) as $keys => $valuess){
echo $valuess['category_title'];
?>
<tr class="table_header">
<th><?php echo $catName." - ".$valuess['category_title'];?> </th>
</tr>
<?php
}
}
Problem is 20 has 4 category_title but when i used array_unique Cater Foil Rolls category title of first parent array is not display.
Output only display
Specialist Range not showing (Cater Foil Rolls)
If you are trying to find unique records in this nested array, then I would key your associative array off of the value of "id". If you are only trying to find unique category_title, then I would key off the value of "category_title". In any case I would use the associate array functionality to ensure uniqueness on whatever key you choose. Then you can apply sorting, etc. on the result.
// find first unique records
$result = [];
foreach($parents as $key1=>$child1) {
foreach($child1 as $key2=>$child2) {
if(!isset($result[$child2['id']])) {
$result[$child2['id']] = $child2;
}
}
}
// result = # => record
OR:
// find first unique categories
$result = [];
foreach($parents as $key1=>$child1) {
foreach($child1 as $key2=>$child2) {
if(!isset($result[$child2['category_title']])) {
$result[$child2['category_title']] = $child2;
}
}
}
// result = category => record
I'm not convinced that array_unique works on multi-dimensional arrays.
See this question.

duplicate an existing tree branch and store it into the database

Hallöschen everyone,
i'm having a hierarchical array which i'm getting from jsTree when I copy+paste a node and I want to insert the new tree branch into database.
My table has an adjacency list structure with the three fields 'id','text','parent_id'.
The structure can be like below or more nested. I did't figured out how to keep track of the parent so to know which children belong to which parent as I walk the array recursively.
What could be the cleanest approach?
Array(
[id] => 1
[text] => 'a'
[children]
=>Array(
[0] => Array
(
[id] => 2
[text] => 'b'
[children] => []
)
[1] => Array
(
[id] => 3
[text] => 'c'
[children] => Array(
[0] => Array(
[id] => 4
[text] =>''
[children]=>[]
)
)
)
)
)
So the final sollution that works for me is this one
function insertRows($data, $parent_id)
{
foreach ($data as $key => $value) {
if(isset($value['text']))
// here insert the the $value['text'] and get the generated $id
if (isset($value['children'])) {
insertRows($value['children'], $id);
}
}
}
To duplicate the dataset try this:
function insertRows($array, $parentId = null)
{
/** #var PDO $pdo */
global $pdo;
foreach ($array as $key => $value) {
// Add text and parent id to table.
$pdo->query('INSERT INTO table SET text = "' . $value['text'] . '", parent_id = ' . $parentId);
if (is_array($value['children'])) {
insertRows($value['children'], $pdo->lastInsertId());
}
}
}
Its untested an similar to pseudocode. Its more a hint.
You can have third index as parent_id and for root element make parent_id as 0 and in thier respective children put parent_id as id of parent.
for root element :
Array(
[id] => 1,
[text] => 'a',
[parent_id] => 0
);
for 1st child it would be like
Array(
[id] => 2,
[text] => 'a',
[parent_id] => 1
);

Create simple array with category tree structure

I would like to create a category-tree-array, with unlimited subcategories. Without having to nest foreach loops in foreach loops.
The goal is to have an array of categories in the right order, that I can use for multiple other functions. I need to be able to easily go through this array to find more underlying data. For instance to fetch and later display the (blog, page or product) items that reside in these categories.
It should look something like this:
Array
(
[0] => Array
(
[url] => url_to_cat
[title] => Top Category 1
[depth] => 0
)
[1] => Array
(
[url] => url_to_cat
[title] => Top Category 2
[depth] => 0
)
[2] => Array
(
[url] => url_to_cat
[title] => Sub Category 1 of Category 2
[depth] => 1
)
[3] => Array
(
[url] => url_to_cat
[title] => Sub Category 2 of Category 2
[depth] => 1
)
[4] => Array
(
[url] => url_to_cat
[title] => Sub Category 1 of Sub Category 2
[depth] => 2
)
)
With some help from this and other sites I have come to this function below, wich is in the right direction, but it gets me a multi-dimensional array. That will be difficult for displaying.
The category table has these fields: cat_id, parent_id, title.
The url comes from another table, is not really relevant here.
function category_list($category_parent_id = 0) {
static $cats;
if (!is_array($cats)) {
$sql = "SELECT * FROM category";
$result = $db->query($sql);
while ($record = $result->fetch_array()) {
$cats[] = $record;
}
}
foreach ($cats as $cat) {
// if not a match, move on
if ((int) $cat['parent'] !== (int) $category_parent_id) {
continue;
}
$item[$i]['url'] = 'url';
$item[$i]['title'] = $cat['title'];
$item[$i]['children'] = category_list($cat['cat_id']);
$list_items[] = $item;
}
return $list_items;
}
The initial $cats array:
Array
(
[0] => Array
(
[title] => Top Category 1
[parent] => 0
[cat_id] => 1
)
[1] => Array
(
[title] => Top Category 2
[parent] => 0
[cat_id] => 2
)
[2] => Array
(
[title] => Sub Category 1 of Category 2
[parent] => 2
[cat_id] => 3
)
[3] => Array
(
[title] => Sub Category 2 of Category 2
[parent] => 2
[cat_id] => 4
)
[4] => Array
(
[title] => Sub Sub Category 1 of Sub Category 2
[parent] => 4
[cat_id] => 5
)
[5] => Array
(
[title] => Sub Sub Sub Category 1 of Sub Sub Category 1
[parent] => 5
[cat_id] => 6
)
)
I can not get my head arround how to get the children included in the main array instead of them being a nested array.
I have searched but can not find the right solution for this on here, so I am sorry if it turns out to be a duplicate. Then I would like to get the link to the original question.
I have found it!
The solution was to make the array global that I use to store the list items in.
Also I have added $level to the function, so that I can display a specific class style per depth.
And finally the recursive use of the function is not stored in the array as a nested "children array", but gets passed on to the global array that I return in the end.
This gives me exactly the right array:
function category_list($category_parent_id = 0, $level = 0) {
// build our category list only once
static $cats;
global $list_items
if (!is_array($cats)) {
$sql = "SELECT * FROM category";
$result = $db->query($sql);
while ($record = $result->fetch_array()) {
$cats[] = $record;
}
}
foreach ($cats as $cat) {
// if not a match, move on
if ((int) $cat['parent'] !== (int) $category_parent_id) {
continue;
}
$list_items[] = array (
'title' => $cat['title'],
'id' => $cat['cat_id'],
'level'=> $level
);
category_list($cat['cat_id'], $level + 1);
}
return $list_items;
}
Note: Url is not used in this second test, but that is not relevant for the example.
Pfew. Finally.

Loop logic help needed - Can't compare correctly

I have the following table:
And I use this function to get data from it:
function get_cart_by($player_id)
{
global $db;
$sql = 'SELECT DISTINCT(item_id) FROM ' . PCP_MARKET_CART . '
WHERE player_id = ' . (int) $player_id;
$result = $db->sql_query($sql);
$rowset = $db->sql_fetchrowseT($result);
$db->sql_freeresult($result);
$cart = array();
foreach ($rowset as $item)
{
$cart[] = $item['item_id'];
}
return $cart;
}
The result looks like this:
Array
(
[0] => 16
[1] => 17
[2] => 49
[3] => 48
[4] => 18
[5] => 19
[6] => 51
)
Now I have an array that lists all my products from another table without looking at the player_id. I want to use the array demonstrated above and add a custom class to the items that do not use the player_id, like show which items the user already has on cart.
The other array that lists all the products looks like this:
Array
(
[0] => Array
(
[item_id] => 16
[parent_id] => 11
[cat_position] => 0
[item_position] => 1
[item_type] => product
[item_title] => Custom Business
[item_description] => Some description
[item_price] => 9.99
[item_units] => 500
[item_preview] => http://i.imgur.com/3eCpMMm.png
[times_sold] => 0
[daopay_url] => http://i.imgur.com/QA7bBfJ.jpg
[public] => 1
[time] => 1384709635
)
[1] => Array
(
[item_id] => 17
[parent_id] => 11
[cat_position] => 0
[item_position] => 1
[item_type] => product
[item_title] => Custom Business
[item_description] => Some description
[item_price] => 9.99
[item_units] => 500
[item_preview] => http://i.imgur.com/3eCpMMm.png
[times_sold] => 0
[daopay_url] => http://i.imgur.com/QA7bBfJ.jpg
[public] => 1
[time] => 1384709635
)
[2] => Array
(
[item_id] => 49
[parent_id] => 11
[cat_position] => 0
[item_position] => 1
[item_type] => product
[item_title] => Custom Business
[item_description] => Some description
[item_price] => 9.99
[item_units] => 500
[item_preview] => http://i.imgur.com/3eCpMMm.png
[times_sold] => 0
[daopay_url] => http://i.imgur.com/QA7bBfJ.jpg
[public] => 1
[time] => 1384709635
)
)
Now based on the first array, I want to mark the same item IDs on the second arrays and show that they are different (on cart).
I have tried quite a lot and for some reason, I managed to mark only item_id 16 and 17, the rest are not getting "marked" for some reason.
This is the code I used:
$cartar = $market->get_cart_by($user->data['player_id']);
$cartln = sizeof($cartar) - 1;
// Fetch items of the selected category
$items = $market->fetch_cat_items($cat_id); // Equivalent to the array above
$index = 0;
print_r($items);
foreach ($items as $item)
{
$name = $item['item_name'];
if ($cartln >= $index)
{
if ($cartar[$index] == $item['item_id'])
$name .= $cartar[$index];
}
echo $name;
$index++;
}
I tried to make the example explain my case the best way possible. So, when I echo out $name it only outputs thename16 and thename17 (those two), but it doesn't continue to 49 and so on.
Please be aware that the array with all the products in it is quite large, I made it shorter for demonstration purposes only.
Were am I failing in my code? Why are only the first two items getting "marked"? I'm quite in a hurry right now, once I get back from a meeting I'll try to explain my issue further.
i don't know if i understood correctly but maybe you want to do it like this:
foreach ($items as $item) {
$name = $item['item_name'];
if(in_array($item['item_id'],$cartar)) {
$name .= $item['item_id'];
}
echo $name;
}
used in_array() to check if the item_id exists somewhere in $cartar. No matter on which position in array.

Recursively creating a multi-dimensional array in PHP

I am trying to figure out the best way to write a PHP function that will recursively build a multi-dimensional array with an unknown number of sublevels from a mysql table. Its purpose is to create a data structure which can be looped through to create a navigation menu on a website, with each menu item possibly having a submenu with child menu items.
The fields of note in the table are:
int ItemID
int ParentID
varchar ItemText
text ItemLink
tinyint HasChildren
So an example of a returned array from the function would be:
$menuItems =
array(
itemID# =>
array(
'ItemText' => 'Home',
'ItemLink' => 'index.php',
'Children' => array(
itemID# => array (
'ItemText' => 'Home Sub 1',
'ItemLink' => 'somepage.php',
'Children' => 0
),
itemID# => array (
'ItemText' => 'Home Sub 2',
'ItemLink' => 'somepage2.php',
'Children' => 0
),
)
),
itemID# =>
array(
'ItemText' => 'Contact',
'ItemLink' => 'contact.php',
'Children' => 0
)
)
);
Would greatly appreciate if someone could point me in the right direction to accomplish this. Thanks!
Not too hard. What you do is store the menu items in an array where you can look them up by ID. Then you iterate over the menu items and if they have a non-null ParentID you add them to their parent's list of children. Then you remove all the children from the master list so you have only top-level items left.
Code:
<?php
$menuItems = array
(
1 => array
(
'ItemText' => 'Home',
'ItemLink' => 'index.php',
'ParentID' => null,
),
2 => array
(
'ItemText' => 'Home Sub 1',
'ItemLink' => 'somepage.php',
'ParentID' => 1,
),
3 => array
(
'ItemText' => 'Home Sub 2',
'ItemLink' => 'somepage2.php',
'ParentID' => 1,
),
4 => array
(
'ItemText' => 'Contact',
'ItemLink' => 'contact.php',
'ParentID' => null,
),
);
// Each node starts with 0 children
foreach ($menuItems as &$menuItem)
$menuItem['Children'] = array();
// If menu item has ParentID, add it to parent's Children array
foreach ($menuItems as $ID => &$menuItem)
{
if ($menuItem['ParentID'] != null)
$menuItems[$menuItem['ParentID']]['Children'][$ID] = &$menuItem;
}
// Remove children from $menuItems so only top level items remain
foreach (array_keys($menuItems) as $ID)
{
if ($menuItems[$ID]['ParentID'] != null)
unset($menuItems[$ID]);
}
print_r($menuItems);
?>
Output:
Array
(
[1] => Array
(
[ItemText] => Home
[ItemLink] => index.php
[ParentID] =>
[Children] => Array
(
[2] => Array
(
[ItemText] => Home Sub 1
[ItemLink] => somepage.php
[ParentID] => 1
[Children] => Array
(
)
)
[3] => Array
(
[ItemText] => Home Sub 2
[ItemLink] => somepage2.php
[ParentID] => 1
[Children] => Array
(
)
)
)
)
[4] => Array
(
[ItemText] => Contact
[ItemLink] => contact.php
[ParentID] =>
[Children] => Array
(
)
)
)
Have a function that calls itself every time it gets an array element.
As in:
Your function is called to display a node. Then it checks if the node its calling from has a sub menu, and if does, it calls itself again. And the process repeats until it dies out, and all the previous function calls return.
void printData($mysql_table_node){
if($mysql_table_node.has_node()){
for($i = 0; $i < $mysqql_table_node.num_nodes()){
printData($mysql_table_node->own_node);
}
}
return;
}
Multi-dimensional tree and an unordered HTML list generator
Recursively build a tree from a multi-dimensional array.
Generate a multi-dimensional HTML list code, from the tree (1).
You can't never know how many dimensions is in the given list of items.
Each element can have a son->Grandson->Great grandson an so on.
So, you must use a recursive function to generate a multi-dimensional list.
Here is the code:
<?php
$categories = array(
'1'=> array('name'=>'one','parent'=>null),
'2'=> array('name'=>'two','parent'=>null),
'20'=> array('name'=>'twenty','parent'=>'2'),
'21'=> array('name'=>'twenty one','parent'=>'2'),
'210'=> array('name'=>'two hundred and ten', 'parent'=>'21'),
'211'=> array('name'=>'two hundred and eleven', 'parent'=>'21'),
'212'=> array('name'=>'two hundred and twelve', 'parent'=>'21')
);
$tree=Menu::CreateTree($categories);
print_r($tree);
Menu::GenerateMenuHtmlCode($tree);
class Menu
{
public static function GenerateMenuHtmlCode($tree)
{
echo '<ul>';
foreach ($tree as $key=>$value)
{
echo "<li>".$value['name'];
if(!empty($value['sons']))
self::GenerateMenuHtmlCode($value['sons']);
echo "</li>";
}
echo '</ul>';
}
public static function CreateTree($categories)
{
$tree=array();
self::AddElement(&$categories,&$tree,null);
return $tree;
}
private function AddElement($categories,&$tree,$parent)
{
foreach ($categories as $key=>$value)
{
if($value['parent']==$parent)
{
$tree[$key]=$categories[$key];
$tree[$key]['sons']=array();
self::AddElement($categories,&$tree[$key]['sons'],$key);
}
if(empty($tree['sons'])) unset ($tree['sons']);
}
unset($categories[$parent]);
return ;
}
}
?>
The result:
Array
(
[1] => Array
(
[name] => one
[parent] =>
[sons] => Array()
)
[2] => Array
(
[name] => two
[parent] =>
[sons] => Array
(
[20] => Array
(
[name] => twenty
[parent] => 2
[sons] => Array()
)
[21] => Array
(
[name] => twenty one
[parent] => 2
[sons] => Array
(
[210] => Array
(
[name] => two hundred and ten
[parent] => 21
[sons] => Array()
)
[211] => Array
(
[name] => two hundred and eleven
[parent] => 21
[sons] => Array()
)
[212] => Array
(
[name] => two hundred and twelve
[parent] => 21
[sons] => Array()
)
)
)
)
)
)
and:
<ul>
<li>one</li>
<li>two
<ul>
<li>twenty</li>
<li>twenty one
<ul>
<li>two hundred and ten</li>
<li>two hundred and eleven</li>
<li>two hundred and twelve</li>
</ul>
</li>
</ul>
</li>
</ul>
I know this is a late reply but I found this script above and it was fantastic. I ran into an issue though with it unsetting my children because of the way the ItemID was working, when I ran it with a similarly designed table to the OP. To get around this, and given the amount of RAM in most web servers should be able to handle this, I've taken John Kugelman's great example and modified it slightly. Instead of having to apply children to all of the items and then unsetting them after, I create a new array and build it all in one
Code:
$new_array = array();
foreach ($menuItems as $key => &$menuItem) {
if (($menuItem['ParentID'] != NULL) && ($menuItem['ParentID'] != '')) {
$new_array[$menuItem['ParentID']]['Children'][$menuItem['ItemID']] = &$menuItem;
} else {
$new_array[$menuItem['ItemID']] = &$menuItem;
}
}
print_r($new_array);
Hope this helps someone else because the above certainly helped me

Categories