Create a JSON tree view in PHP using delimiter - php

I am trying to create a file explorer type treeview JSON to be read by FancyTree for a project I'm attempting.
The files are stored in a database, with an ID, name, URL, Type and code fields. The mock database looks like this:
ID name URL. Type code
1 test dir.dir1 txt sometext
2 next dir.dir1 txt somemoretext
3 main dir txt evenmoretext
I need to build the JSON tree view from this data, using the URL as a path (period being the delimiter) and the files being inside the final directory so the tree looks like
/dir/dir1/test.txt
/dir/dir1/next.txt
/dir/main.txt
FancyTree JSON output should look like
[
{
"title": "dir",
"folder": true,
"children": [
{
"title": "dir1",
"folder": true,
"children": [
{
"title": "test.txt",
"key": 1
}, {
"title": "next.txt",
"key": 2
}
]
}, {
"title": "main.txt",
"key": 3
}
]
}
]
Currently, I'm getting the data from the database into $scriptArray
SELECT 'name','url','type','id' FROM.....
I'm then sorting and building a tree with
$url = array_column($scriptArray, 'url');
array_multisort($url, SORT_ASC, $scriptArray);
$result = [];
foreach($scriptArray as $item) {
$loop = 0;
$keys = array_reverse(explode('.', $item->url));
$tmp = $item->name;
$tmp2 = $item->type;
foreach ($keys as $keyid => $key) {
if($loop == 0) {
$tmp = ["title" => $tmp.".".$tmp2, 'key' => $item->id];
} else {
$tmp = ["title" => $keys[$keyid - 1], "folder" => true, "children" => [$tmp]];
}
$loop++;
}
$tmp = ["title" => $keys[count($keys)-1], "folder" => true, "children" => [$tmp]];
$result[] = $tmp;
}
However, the output I'm getting is.
[
{
"title": "dir",
"folder": true,
"children": [
{
"title": "dir2",
"folder": true,
"children": [
{
"title": "test.txt",
"key": 1
}
]
}
]
},
{
"title": "dir",
"folder": true,
"children": [
{
"title": "dir2",
"folder": true,
"children": [
{
"title": "next.txt",
"key": 2
}
]
}
]
},
{
"title": "main.txt",
"key": 3
}
]
I have tried applying an array_merge, array_merge_recursive and various others without success. Can anyone help with this?

Working with loop won't work unless you know per advance the maximum depth of your folder hierarchy.
A better solution is to build the folder path "recursively", and append the file to the final folder.
This can be achieved with by creating a reference with the & operator, and navigate to its children until the whole path is build :
$result = array();
foreach($files as $file)
{
// build the directory path if needed
$directories = explode('.', $file->url); // get hierarchy of directories
$currentRoot = &$result ; // set the pointer to the root directory per default
foreach($directories as $directory)
{
// check if directory already exists in the hierarchy
$dir = null ;
foreach($currentRoot as $i => $d)
{
if(isset($d['folder']) && $d['folder'] and $d['title'] == $directory)
{
$dir = &$currentRoot[$i] ;
break ;
}
}
// create directory if missing
if(is_null($dir))
{
$item = array(
'title' => $directory,
'folder' => true,
'children' => array()
);
$currentRoot[] = $item ;
$dir = &$currentRoot[count($currentRoot)-1];
}
// move to the next level
$currentRoot = &$dir['children'] ;
unset($dir);
}
// finally append the file in the latest directory
$currentRoot[] = array(
'title' => $file->name . '.' . $file->type,
'key' => $file->id,
);
unset($currentRoot);
}
echo json_encode($result);

Related

get parent nodes from a node tree array

[
{
"id": 1573695284631,
"name": "Cars",
"pid": 0,
"children": [
{
"id": 1573695292010,
"name": "Audi",
"pid": 1573695284631
},
{
"id": 1573695305619,
"name": "BMW",
"pid": 1573695284631,
"children": [
{
"id": 1573695328137,
"name": "3 Series",
"pid": 1573695305619
},
{
"id": 1573695335102,
"name": "X5",
"pid": 1573695305619
}
]
}
]
},
{
"id": 1573695348647,
"name": "Motorcycles",
"pid": 0,
"children": [
{
"id": 1573695355619,
"name": "Ducatti",
"pid": 1573695348647
}
]
}
]
Suppose I have this node-tree-like array in PHP (represented above in json for readability). For a given child node ID, I would like to find all parent node IDs that it's nested under. For example,
getParentNodes($haystack, $child_node_id=1573695328137); //[1573695284631, 1573695292010, 1573695305619]
I assume this is a use case for recursion. Here's my best attempt:
function getParentNodes($haystack, $child_node_id) {
if( empty($haystack->children) )
return;
foreach($haystack->children as $child) {
if($child->id == $child_node_id) {
// $child found, now recursively get parents
} else {
getParentNodes($child, $child_node_id);
}
}
}
This one will walk the tree until it hits the desired id.
In all cases where the leaf is not the desired one, it will return false - and collapse up the stack resulting in false or an array of parent-ids if the child is found.
Code
function getParentNodes($haystack, $child_node_id) {
foreach ($haystack as $element) {
if ($element['id'] === $child_node_id) {
// return [$element['id']]; // uncomment if you want to include child itself
return [];
} else if (array_key_exists('children', $element)) {
$parentNodes = getParentNodes($element['children'], $child_node_id);
if ($parentNodes !== false) {
return [$element['id'], ...$parentNodes];
}
}
}
return false;
}
Outputs parent ids:
array(2) {
[0]=>
int(1573695284631)
[1]=>
int(1573695305619)
}
Working example.
You missing return result. This is what you want.
function getParentNodes($arr, $child_node_id) {
$result = [];
foreach($arr as $item) {
if($item->pid == $child_node_id) {
$result[] = $item->id;
}
if(!empty($item->children)) {
$result[] = getParentNodes($item->children, $child_node_id);
}
}
return $result;
}
also you need get values as flat array
$values = getParentNodes($values, 1573695284631);
// do flat arr
array_walk_recursive($values,function($v) use (&$result){ $result[] = $v; });
// your values
var_dump($result);
Source reference for flat array
I ended up writing 2 recursive functions
function treeSearch($needle, $haystack) {
foreach($haystack as $node) {
if($node->id == $needle) {
return $node;
} elseif ( isset($node->children) ) {
$result = treeSearch($needle, $node->children);
if ($result !== false){
return $result;
}
}
}
return false;
}
treeSearch will find the node in the tree, then I need to recursively go up the tree until the parent id (pid) is 0
function getParents($node, $hierarchy, $all_parent_ids=[]) {
if($node->pid != 0) {
$parent_node = treeSearch($node->pid, $hierarchy);
$all_parent_ids[] = $parent_node->id;
$result = getParents($parent_node, $hierarchy, $all_parent_ids);
if ($result !== false){
return $result;
}
}
return $all_parent_ids;
}
then, supposing the tree is called $tree I can call them like:
$node = treeSearch(1573695328137, $tree);
$parents = getParents($node, $tree);
This is the best solution to take the parent of a child !
function getPathParent($id, $tree='',$opts='', &$path = array()) {
$in = array_replace(array(
'id'=>'id',
'child'=>'children',
'return'=>'id'
),(array)$opts);
if ( is_array($tree) && !empty($tree) ){
foreach ($tree as $item) {
if ($item[$in['id']] == $id) {
array_push($path, $item[$in['return']]);
return $path;
}
if ( isset($item[$in['child']]) && !empty($item[$in['child']]) ) {
array_push($path, $item[$in['return']]);
if (getPathParent($id, $item[$in['child']],$opts, $path) === false) {
array_pop($path);
} else {
return $path;
}
}
}
}
return false;
}
$tree = [
[
"id" => 1573695284631,
"name" => "Cars",
"pid" => 0,
"children" => [
[
"id" => 1573695292010,
"name" => "Audi",
"pid" => 1573695284631
],
[
"id" => 1573695305619,
"name" => "BMW",
"pid" => 1573695284631,
"children" => [
[
"id" => 1573695328137,
"name" => "3 Series",
"pid" => 1573695305619
],
[
"id" => 1573695335102,
"name" => "X5",
"pid" => 1573695305619
]
]
]
]
],
[
"id" => 1573695348647,
"name" => "Motorcycles",
"pid" => 0,
"children" => [
[
"id" => 1573695355619,
"name" => "Ducatti",
"pid" => 1573695348647
]
]
]
];
$getParentNode = getPathParent(1573695335102,$tree);
var_export($getParentNode);
// return:
/*
array (
0 => 1573695284631,
1 => 1573695305619,
2 => 1573695335102,
)
*/
$getParentNode = getPathParent(1573695335102,$tree,array('return'=>'name'));
var_export($getParentNode);
// return:
/*
array (
0 => 'Cars',
1 => 'BMW',
2 => 'X5',
)
*/
$getParentNode = getPathParent(1573695335102,$tree,array('id'=>'id','child'=>'children','return'=>'pid'));
var_export($getParentNode);
// return:
/*
array (
0 => 0,
1 => 1573695284631,
2 => 1573695305619,
)
*/

Populate PHP associative array using loop with mysql data

I'm creating simple PHP for output data to apexcharts javascript charts. To make apexcharts usable output I need to provide x and y value of the graphs as JSON. below code, I wrote to output the expected JSON.
$data_arr = array();
global $mysqli_conn;
$result = $mysqli_conn->query("sql");
$sql_out = array();
$sql_out = $result->fetch_all(MYSQLI_ASSOC);
$num_rows = mysqli_num_rows($result);
if ($num_rows < 1){
echo "zero";
}else{
foreach($sql_out as $item) {
$data_arr['x'][] = $item['time'];
$data_arr['y'][] = $item['status_code'];
}
}
$test_arr = array(
array(
"name"=>"lock",
"data"=>array($data_arr),
)
);
echo json_encode($test_arr);
my expected json output is like below
[
{
"name": "lock",
"data": [
{
"x": "2019-05-30 07:53:07",
"y": "1470"
},
{
"x": "2019-05-29 07:52:27",
"y": "1932"
}
]
}
]
But when I request data from my code what I'm getting something like this
[
{
"name": "lock",
"data": [
{
"x": [
"2019-05-30 07:53:07",
"2019-05-29 07:52:27",
"2019-05-26 15:46:56",
"2019-05-25 07:39:24"
],
"y": [
"1470",
"1932",
"1940",
"1470"
]
}
]
}
]
How can I create my expected JSON result from PHP code?.
You're creating them on a seprate space. When you declare and push them, put them on one container:
foreach ($sql_out as $item) {
$data_arr[] = array(
'x' => $item['time'],
'y' => $item['status_code']
);
}
When you do this:
$data_arr['x'][] = $item['time'];
$data_arr['y'][] = $item['status_code'];
They are on a separate containers, x and y containers, therefore you get the wrong format, like the one you showed.
When you declare them as:
$data_arr[] = array(
'x' => $item['time'],
'y' => $item['status_code']
);
You're basically tellin to push the whole sub batches but together.

create embedded json for autocomplete

I am using following code for making data coming from database as json format
public function employeeSearch()
{
$arrayOfEmployee = array();
$arrayToPush = array();
$arrayToJSON = array();
$new_item = $this->apicaller->sendRequest(array(
"controller" => "Employee",
"action" => "employeeSearch",
"searchCriteria" => "12345"
));
$arrayOfEmployee = json_decode($new_item,true);
foreach($arrayOfEmployee as $key => $employee)
{
$arrayToPush = array('data' => $employee['FullName'], 'value' => $employee['_id']['$oid']);
array_push($arrayToJSON, $arrayToPush);
}
echo json_encode($arrayToJSON);
}
The output is
[{"data":"Aasiya Rashid Khan","value":"5aa662b0d2ccda095400022f"},
{"data":"Sana Jeelani Khan","value":"5aa75d8fd2ccda0fa0006187"},
{"data":"Asad Hussain Khan","value":"5aaa51ead2ccda0860002692"},
{"data":"Ayesha Khan Khann","value":"5aab61b4d2ccda0bc400190f"},
{"data":"adhar card name","value":"5aaba0e1d2ccda0bc4001910"}
]
Now I want that json elements should look like
{
"suggestions": [
{
"value": "Guilherand-Granges",
"data": "750"
},
{
"value": "Paris 01",
"data": "750"
}
]
}
I have to implement this in jQuery autocomplete plugin...
Please help!!!
Replace the last line with
echo json_encode(["suggestions" => $arrayToJSON]);
This should result in the wanted result!
(This hold only true if you igonre the fact that the data in value and name is not the same/similar)

Create 2D-Array from mysql query in php

I have this following result in my query:
I'm trying to create an array like this in php:
[
{
"software_version": "1.0",
"version_date": "10/08/2016",
"changelog": [
{
"type": "IMP",
"description": "Initial version."
}
]
},
{
"software_version": "1.0.1",
"version_date": "27/07/2017",
"changelog": [
{
"type": "ADD",
"description": "HostPanel update manager."
},
{
"type": "ADD",
"description": "Hook OnDaemonMinute."
}
]
}
]
I need to combine the result with the software_version row.
Any help is appreciated.
My php code:
$changelog = array();
foreach ($result as $r) {
$changelog[] = array(
'software_version' => $r['software_version'],
'version_date' => $r['version_date'],
'changelog' => array(
array(
'type' => 'IMP', // help
'description' => 'Initial version.'
)
)
);
}
The key is to use the software version as a key in $changelog as you build it.
$changelog = array();
foreach ($result as $r) {
// get the version (just to make the following code more readable)
$v = $r['software_version'];
// create the initial entry for the version if it doesn't exist yet
if (!isset($changelog[$v]) {
$changelog[$v] = ['software_version' => $v, 'version_date' => $r['version_date']];
}
// create an entry for the type/description pair
$change = ['type' => $r['type'], 'description' => $r['description']];
// add it to the changelog for that version
$changelog[$v]['changelog'][] = $change;
}
You'll need to use array_values to reindex $changelog before JSON encoding it in order to produce the JSON array output you're going for.
$changelog = array_values($changelog);

Get parent values from multidimensional associative json file

I have a multi- and variable-level json file that looks like the example at the bottom of this post. What I need to do is search for a particular value, let's say "Gap junction" and return the value for "name" for all of the higher level parents, in this example "Junction", "Plasma membrane" and "Cell". I need to do this using php and I think I need a recursive loop to traverse the array that will record the "name" value for each depth level into an array and then return this "name" array once the search term is found, but I'm struggling a bit at achieving this. Any help would be appreciated.
{
"name": "Cell",
"children": [
{
"name": "Plasma membrane",
"children": [
{
"name": "Junction",
"children": [
{"name": "Adherens junction"},
{"name": "Caveola"},
{"name": "Gap junction"},
{"name": "Lipid raft"},
{"name": "Tight junction"}
]
},
{"name": "Focal adhesion"}
]
},
{
"name": "Vesicle",
"children": [
{
"name": "Endosome",
"children": [
{"name": "Early Endosome"},
{"name": "Late Endosome"},
{"name": "Recyling Endosome"}
]
},
{ "name": "Microsome"}
]
}
]
}
EDIT
Current code as requested. The $found variable is certainly not working how I intended it too. Code is based/modified on this answer: Get Parent and Child depth level from JSON using PHP?
$jsonString = file_get_contents("./information/localization.json");
$jsonArray = json_decode($jsonString);
$currOrganelle = "Gap junction";
$parents = read_tree_recursively($jsonArray, $currOrganelle);
function read_tree_recursively($items, $searchTerm, $result = array(), $level = 0, $found = false) {
foreach($items as $child) {
if(!$found) {
$currName = $child->name;
if($currName == $searchTerm) {
$found = true;
return $result;
}
elseif(!empty($child->children)) {
$result[$level] = $currName;
$result = read_tree_recursively($child->children, $searchTerm, $result, $level + 1, $found);
if($found) return $result;
}
else {
}
}
else {
return $result;
}
}
}
Solution with RecursiveIteratorIterator and RecursiveArrayIterator classes:
// $str - is your initial json string
$decoded = json_decode($str, TRUE);
function getParentNameKeys($arr = [], $needle = "") {
$iterator = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($arr));
$nameKeys = [];
foreach ($iterator as $key => $value) {
if ($value === $needle) {
$depth = $iterator->getDepth();
while ($depth--){
if ($iterator->getSubIterator($depth)->offsetExists('name')) {
$nameKeys[] = $iterator->getSubIterator($depth)->offsetGet('name');
}
}
}
}
return $nameKeys;
}
$nameKeys = getParentNameKeys($decoded, "Gap junction");
var_dump($nameKeys);
// the output:
array (size=3)
0 => string 'Junction' (length=8)
1 => string 'Plasma membrane' (length=15)
2 => string 'Cell' (length=4)
$nameKeys = getParentNameKeys($decoded, "Early Endosome");
var_dump($nameKeys);
// the output:
array (size=3)
0 => string 'Endosome' (length=8)
1 => string 'Vesicle' (length=7)
2 => string 'Cell' (length=4)
http://php.net/manual/en/class.recursiveiteratoriterator.php

Categories