I'm trying to build a multidimensional associative array while fetching the results from a MySQL query.
But i can't figure out how to achieve this.
I want to build an array like this one:
array ("cat_name" => "category1",
"id_cat" => "1",
"sub_cat" => array ("name_sub_cat" => "subCategory",
"id_sub_cat" => "4",
"ss_cat" =>array ("ss_cat_name" =>"ss_cat1",
"id_ss_cat" => "4"
)
)
);
Here's where i'm building the array:
Edit : I've ended up with something like that, not very sexy, but if think i'm almost there
while($row = mysql_fetch_assoc($resultQueryCat)){
$menuArray[$row["id_cat"]]["cat_name"] = $row["cat_name"];
$menuArray[$row["id_cat"]][$row["id_sub_cat"]]["id_sub_cat"] = $row["id_sub_cat"];
$menuArray[$row["id_cat"]][$row["id_sub_cat"]]["name_sub_cat"] = $row["name_sub_cat"];
$menuArray[$row["id_cat"]][$row["id_sub_cat"]][$row["ss_cat_name"]]["ss_cat_name"] = $row["ss_cat_name"];
$menuArray[$row["id_cat"]][$row["id_sub_cat"]][$row["ss_cat_name"]]["id_ss_cat"] = $row["id_ss_cat"];
}
Edit2: The code to display the array
$menu.='<ul>';
foreach ($menuArray as $key) {
$compteur_cat++;
$menu.= '<div id="collapsiblePanelCol'.$compteur_cat.'" class="collapsiblePanelCol floatleft">
<li class="categorie"> '.$key["cat_name"].'
<ul>';
foreach ($key as $key1) {
if (is_array($key1){/* One of the thing i forgot which totally screwed up my results*/
$menu.= '<ul>
<li class="ss_categorie">'.$key1["name_sub_cat"].'<ul>';
foreach ($key1 as $key2) {
if (is_array($key2)){
$menu.= '<li class="element">'.$key2["ss_cat_name"].'</li>'; }
}
$menu.= '</ul></li></ul>';
}
}
$menu.='</ul>
</li>
</div>';
}
$menu.= '</ul>';
Thanks.
Final Edit: My code is working :) i edited the previous code to make it correct
It's not an easy task, as data from DB might contain loops :-)
I'd better save it in id->data hashmap so that you can build the structure easier.
Not sure why you need multidimensions.
The way I build multidimensional arrays is through classes and their objects.
Example:
class Categories
{
public $cat_name;
public $id_cat;
function __construct($cat_name, $id_cat)
{
$this->id = $id_cat;
$this->name = $cat_name;
$this->sub_cat = array();
}
}
class SubCategories
{
public $name_sub_cat;
public $id_sub_cat;
function __construct($name_sub_cat, $id_sub_cat)
{
$this->id = $id_sub_cat;
$this->name = $name_sub_cat;
$this->ss_cat = array();
}
}
class SSCategories
{
public $ss_cat_name;
public $id_ss_cat;
function __construct($ss_cat_name, $id_ss_cat)
{
$this->id = $id_ss_cat;
$this->name = $ss_cat_name;
}
}
Then to write to those classes (after fetching MySQL DB Data [assuming OOP mysql]):
if($result)
{
$array = array();
while($row = $result->fetch_array())
{
$array[] = new Categories($row["cat_name"], $row["id_cat"]);
}
foreach($array as $main)
{
... (fetching sub category info)
if($sub_result)
{
$sub_array = array();
while($sub_row = $sub_result->fetch_array())
{
$sub_array[] = new SubCategories($sub_row["name_sub_cat"], $sub_row["id_sub_cat"]);
}
$main->sub_cat = $sub_array;
You would repeat for each within original if loop for as many sub arrays as you have. This took a while :).
NOTE: Close if loops (heirarchally) if there are no more sub arrays.
echo '<pre>';
print_r($array);
echo '</pre>';
The above code will help show the levels of your array!
Related
How to loop all records and display all the respective children using HTML <ul></ul>? I tried using PHP Do While but stuck at only 1 level.
MySQL (select * from user)
Desired output
Tree View
List View
The easy was is to do that with the help of array. Hope it helps.
$data = array();
foreach ($result as $item) {
$key = $item['name']; // or $item['info_id']
if (!isset($data[$key])) {
$data[$key] = array();
}
$data[$key][] = $item;
}
You can use this code:
$aResults; // it is your mysql result (array)
$resultSorted = array();
$resultSorted = recursiveList($aResults, '');
function recursiveList(&$aResults, $iKey)
{
$aChilds = '<ul>';
foreach ($aResults as $iLoopKey => $aResult) {
if ($aResult['parent'] == $iKey) {
unset($aResults[$iLoopKey]);
$aChilds .= '<li>' . $aResult['name'] . '</li>';
$aChilds .= recursiveList($aResults, $aResult['name']);
}
}
return $aChilds . '</ul>';
}
// Output example
echo '<pre>';
print_r($resultSorted);
As a result, I recived:
Also you'd better use 'parent_id' instead 'parent' in yours tables.
I am trying to handle a situation with a nested multiple array that is received in PHP by $_POST from Javascript-Jquery (as Object not as Json)
. The nested Object looks like this:
{
"Videotheck":{
{
"Category":"Comedy",
"Title_Liste":[
{
"Title":"Millers",
"Year":"2014"
},
{
"Title":"Yogi",
"Year":"2012"
}
]
},
{
"Category":"Action",
"Title_Liste":[
{
"Title":"Rodulf",
"Year":"2014"
},
{
"Title":"Matrix",
"Year":"2000"
}
]
}
}
}
And now the information in this Object need to be splited. For example the title list of each category should be stored in a var
$comedy_title_liste = [];
$action_title_liste = [];
I tryed this:
if($_POST){
$arr1 = $_POST['Videotheck'];
foreach($arr1 as $vtk){
foreach($vtk as $data => $v){
foreach($v as $key => $value){
foreach($value as $k => $info){
echo $k.' '. $info;
}
}
}
}
}
Like this I can get only all title list from all categories, but is necessary to get for each category the list of titles separeted. I don't know really how to handle the situation.
Well this is what I have. I guest that there is something not correct.
Not 100% exact but you can give a try :
$result = array();
$parent = $_POST['Videotheck'];
foreach($parent as $key=> $child) {
$result[$child['Category']."_title_liste"] = array();
foreach($child['Title_Liste'] as $cKey => $val) {
$result[$child['Category']."_title_liste"][] = $val['Title'];
}
}
I'm relatively new to PHP and I hope you can help me solve my problem. I am selecting out data from a database into an array for timekeeping. Ultimately, I would like to calculate the total number of hours spent on a project for a given customer.
Here is the code to populate a multi-dimensional array:
...
foreach ($record as $data) {
$mArray = array();
$name = $data['user'];
$customer = $data['customer'];
$project = $data['project'];
$hours = $data['hours'];
$mArray[$name][$customer][$project] += $hours;
}
...
I would now like to iterate over $mArray to generate an xml file like this:
...
foreach ($mArray as $username) {
foreach ($mArray[$username] as $customerName) {
foreach ($mArray[$username][$customerName] as $project ) {
echo '<'.$username.'><'.$customerName.'><'.$project.'><hours>'.
$mArray[$username][$customerName][$project].'</hours></'.$project.'>
</'.$customerName.'></'.$username.'>';
}
}
}
This nested foreach doesn't work. Can someone give me a couple of tips on how to traverse this structure? Thank you for reading!
UPDATE:
Based on the comments I've received so far (and THANK YOU TO ALL), I have:
foreach ($mArray as $userKey => $username) {
foreach ($mArray[$userKey] as $customerKey => $customerName) {
foreach ($mArray[$userKey][$customerKey] as $projectKey => $projectName) {
echo '<name>'.$userKey.'</name>';
echo "\n";
echo '<customerName>'.$customerKey.'</customerName>';
echo "\n";
echo '<projectName>'.$projectKey.'</projectName>';
echo "\n";
echo '<hours>'.$mArray[$userKey][$customerKey][$projectKey].'</hours>';
echo "\n";
}
}
}
This is now only providing a single iteration (one row of data).
Foreach syntax is foreach($array as $value). You're trying to use those values as array keys, but they're not values - they're the child arrays. What you want is either:
foreach($mArray as $username) {
foreach($username as ...)
or
foreach($mArray as $key => $user) {
foreach($mArray[$key] as ...)
Following function arranges the array totally wrong. Have you noticed any wrong piece of code in following function?
function buildHtmlList($array)
{
$maxlevel = 0;
foreach ($array as $key => $value)
{
$previousparent = isset($array[$key - 1]['parent']) ? $array[$key - 1]['parent'] : null;
$nextparent = isset($array[$key + 1]['parent']) ? $array[$key + 1]['parent'] : null;
if ($value['parent'] != $previousparent)
{
echo "\n<ul>";
++$maxlevel;
}
echo "\n<li>" . $value['name'];
if ($nextparent == $value['parent'])
echo "</li>";
}
for ($i = 0; $i < $maxlevel; ++$i)
{
echo "\n</li>\n</ul>";
}
}
It arranges the array totally wrong. Have you noticed any wrong piece of code in following function?
The wrong piece is the whole logic of the function. You treat the array as a flat list (as it is!), however, you'd like to display a tree.
As a flat list can't be displayed as a tree, you need to change the flat list to a tree first and then write a function that displays a tree.
An example how to convert a flat array to a tree/multidimensional one is available in a previous answer.
Try something like this (where $array is formatted like your example):
$corrected_array = array();
// This loop groups all of your entries by their parent
foreach( $array as $row)
{
$corrected_array[ $row['parent'] ][] = $row['name'];
}
// This loop outputs the children of each parent
foreach( $corrected_array as $parent => $children)
{
echo '<ul>';
foreach( $children as $child)
{
echo '<li>' . $child . '</li>';
}
echo '</ul>';
}
Demo
I'm creating a tree-structure of categories with parentid's which can be called from children in such a way:
ID | Name | ParentID
1 1 0
2 2 1
3 3 2
4 4 1
Resulting in this:
1 = 1
2 = 1 -> 2
3 = 1 -> 2 -> 3
4 = 1 -> 4
which means 3 is a child of 2, which is a child of 1.
when trying to get this idea (with the -> to show what relations are set) I only get to the second grade (1 -> 2) but not to the third (1->2->3) because of the looping function I use for it.
//put all ID's in an array
while ($row2 = $connector->fetchArray($result2)){
$id = $row2['ID'];
$parents[$id] = $row2['name'];
}
// show the tree-structure
while ($row = $connector->fetchArray($result)){
if($row['parentid']!=0)echo $parents[$row['parentid']].' -> ';
echo $row['name'].' - ';
echo '<br>';
}
I'd like two things to change:
have the code automatically generate a tree sized as necessary.
in the while-loops i have to select the $result twice (once as $result, once as $result2) to make it work. these $result's have exactly the same database-query:SELECT ID,name,parentid FROM categories
to fetch results from. I'd like to only declare this once.
Thanks for all the good answers. I've gone with the easiest, less-code-to-implement approach:
$result = $connector->query('SELECT ID,name,parentid FROM categories');
// Get an array containing the results.
$parents = array();
while ($row = $connector->fetchArray($result)){
$id = $row['ID'];
$parents[$id] = array('ID' => $row['ID'],'name' => $row['name'],'parentid' => $row['parentid']);
}
foreach ($parents as $id => $row){
$pid=$id;
$arrTmp= array();
do { // iterate through all parents until top is reached
$arrTmp[]=$pid;
$pid = $parents[$pid]['parentid'];
}while ($pid != 0);
$arrTmp = array_reverse($arrTmp);
foreach($arrTmp as $id){
echo $parents[$id]['name'].' -> ';
}
echo '<br>';
}
Rather than have PHP organize the items into a tree, why not ask the database to do it for you? I found this article on hierarchical data to be very good and the examples are almost identical to yours.
EDIT
The SQL for getting the full tree using the Adjacency Model is not ideal. As the article explains it requires rather a lot of joins for even a small hierarchy. Is it not possible for you to use the Nested Set approach? The SQL stays the same regardless of the size of the hierarchy and INSERT and DELETE shouldn't be very difficult either.
If you really want to do hierachies with parent ids(suitable only for small number of items/hierachies)
I modified your code a little bit(I did not test it so there may be some syntax errors):
//put all recordsets in an array to save second query
while ($row2 = $connector->fetchArray($result2)){
$id = $row2['ID'];
$parents[$id] = array('name' => $row2['name'],'parent' => $row2['parentid']);
}
// show the tree-structure
foreach ($parents as $id => $row){
$pid = $row['parentid'];
while ($pid != 0){ // iterate through all parents until top is reached
echo $parents[$pid]['name'].' -> ';
$pid = $parents[$pid]['parentid'];
}
echo $parents[$id]['name'].' - ';
echo '<br>';
}
To answer your comment:
$parents = array();
$parents[2] = array('ID'=>2,'name'=>'General','parentid'=>0);
$parents[3] = array('ID'=>3,'name'=>'Gadgets','parentid'=>2);
$parents[4] = array('ID'=>4,'name'=>'iPhone','parentid'=>3);
foreach ($parents as $id => $row){
$pid=$id;
$arrTmp= array();
do { // iterate through all parents until top is reached
$arrTmp[]=$pid;
$pid = $parents[$pid]['parentid'];
}while ($pid != 0);
$arrTmp = array_reverse($arrTmp);
foreach($arrTmp as $id){
echo $parents[$id]['name'].' -> ';
}
echo '<br>';
}
Prints out:
General ->
General -> Gadgets ->
General -> Gadgets -> iPhone ->
Maybe easier with OOP. Just sort the query by parentId
Note: The listChildren method and the printout at the bottom is just there to show it is listed correctly. I did not interpret the question that the display was important.
class Element {
public $id;
public $name;
public $parent = null;
public $children = array();
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
public function addChild($element)
{
$this->children[$element->id] = $element;
$element->setParent($this);
}
public function setParent($element)
{
$this->parent = $element;
}
public function hasChildren()
{
return !empty($this->children);
}
public function listChildren()
{
if (empty($this->children)) {
return null;
}
$out = array();
foreach ($this->children as $child) {
$data = $child->id . ':' . $child->name;
$subChildren = $child->listChildren();
if ($subChildren !== null) {
$data .= '[' . $subChildren . ']';
}
$out[] = $data;
}
return implode(',', $out);
}
}
$elements = array();
$noParents = array();
while ($row = $connector->fetchArray($result)) {
$elements[$row['id']] = $element = new Element($row['id'], $row['name']);
if (isset($elements[$row['parent']])) {
$elements[$row['parent']]->addChild($element);
} else {
$noParents[] = $element;
}
}
foreach ($noParents as $element) {
if ($element->hasChildren()) {
echo "Element {$element->id} has children {$element->listChildren()}.\n";
} else {
echo "Element {$element->id} has no children.\n";
}
}
If you are using PostgreSQL as the database, you can use the connectby() function to create the record set:
SELECT *
FROM connectby('tableName', 'id', 'parent_id')
AS t(keyid text, parent_keyid text, level int);
I love this function, and use all the time in my code. It can do some very powerful things, very quickly, and you don't have maintain the left/right values like the (adjacency model).