show() function returns wrong data,why there is only one children
how to modify recursiveData function
Expectation result:
[
{
"id": 1,
"title": "p1",
"parentId": 0,
"children": [
{
"id": 5,
"title": "p1-child1",
"parentId": 1,
"children": [
{
"id": 13,
"title": "p5-child",
"parentId": 5,
"children": [
{
"id": 14,
"title": "p13-child",
"parentId": 13
}
]
}
]
},
{
"id": 6,
"title": "p1-child2",
"parentId": 1
}
]
},
{
"id": 2,
"title": "p2",
"parentId": 0,
"children": [
{
"id": 9,
"title": "p2-child1",
"parentId": 2
}
]
}
]
public function show()
{
$data = $this->getData(0);
$res = $this->recursiveData($data);
echo json_encode($res);
}
public function recursiveData($data)
{
if (empty($data)) {
return false;
}
$children = [];
foreach ($data as $dk => $dv) {
$children = $this->getData($dv['id']);
$data[$dk]['children'] = $children;
$this->recursiveData($children);
}
return $data;
}
/**
* Get data from database.
* #return array
**/
public function getData($parent_id)
{
$tmp = [];
$data = DB::select('select id,title from category where parent_id<99 and parent_id = ?', [$parent_id]);
foreach ($data as $dk => $dv) {
$tmp[] = [
'id' => $dv->id,
'title' => $dv->title,
'parentId' => $parent_id,
];
}
return $tmp;
}
At recursiveData()
modify
$data[$dk]['children'] = $children;
$this->recursiveData($children);
to
$data[$dk]['children'] = $this->recursiveData($children);
Related
I tried to parse a JSON using PHP from url. I need a list of item that have no children - means children field should be empty array and parent_id shouldn't be 0 - means not be parents.
JSON:
{
"body": {
"data": [
{
"id": 1,
"name": "car",
"parent_id": "0",
"children": [
{
"id": 2,
"name": "bmw",
"parent_id": "1",
"children": [
{
"id": 4,
"name": "bmw i8",
"parent_id": "2",
"children": []
}
]
},
{
"id": 3,
"name": "mustang",
"parent_id": "1",
"children": []
}
]
},
{
"id": 5,
"name": "clothes",
"parent_id": "0",
"children": []
},
{
"id": 6,
"name": "mobile",
"parent_id": "0",
"children": [
{
"id": 7,
"name": "apple",
"parent_id": "6",
"children": [
{
"id": 9,
"name": "Iphone 12 pro",
"parent_id": "7",
"children": []
},
{
"id": 10,
"name": "Iphone 11",
"parent_id": "7",
"children": []
}
]
},
{
"id": 8,
"name": "Xiaomi",
"parent_id": "6",
"children": []
}
]
}
]
}
}
Output Expected:
[4,3,9,10,8]
This is my php code that i tried and doesn't work.
$CategoryUrl = file_get_contents(self::CATEGORY_URL,true);
$array = json_decode($CategoryUrl,true);
$list = array();
foreach( $array['body']['data'] as $value ){
if (($value['parent_id'] != 0) && empty($value['children'])) {
foreach( $value['children'] as $val ){
$list[] = $val;
}
}
}
print_r($list);
It is indeed a little tricky to keep track of parent ID values and children call using array_walk_recursive as it jumps directly to its children. However, this can be still accomplished with your own recursive version like below. Keep checking with parent_id and children count. If both constraints satisfy, add them to $result, else keep calling children recursively.
<?php
$str = '{"body":{"data":[{"id":1,"name":"car","parent_id":"0","children":[{"id":2,"name":"bmw","parent_id":"1","children":[{"id":4,"name":"bmw i8","parent_id":"2","children":[]}]},{"id":3,"name":"mustang","parent_id":"1","children":[]}]},{"id":5,"name":"clothes","parent_id":"0","children":[]},{"id":6,"name":"mobile","parent_id":"0","children":[{"id":7,"name":"apple","parent_id":"6","children":[{"id":9,"name":"Iphone 12 pro","parent_id":"7","children":[]},{"id":10,"name":"Iphone 11","parent_id":"7","children":[]}]},{"id":8,"name":"Xiaomi","parent_id":"6","children":[]}]}]}}';
$data = json_decode($str,true);
$result = [];
function walkRecursive($data,&$result){
foreach($data as $entry){
if($entry['parent_id'] != 0 && count($entry['children']) == 0){
$result[] = $entry['id'];
}else{
walkRecursive($entry['children'],$result);
}
}
}
walkRecursive($data['body']['data'],$result);
print_r($result);
We can solve this problem by function call recursive algorithm,
hope it clear for you
$CategoryUrl = file_get_contents(self::CATEGORY_URL,true);
$array = json_decode($CategoryUrl, true);
$items = $array['body']['data'];
$list = [];
findParentIds(items, $list);
// doSomething
print_r($list);
/**
* passe by ref (list)
* #param array $children
* #param $list
*/
function findParentIds(array $children, & $list)
{
foreach ($children as $child) {
// use case 1: has no children and parent_id is 0 , just continue
if (empty($child['children']) && $child['parent_id'] == "0") {
continue;
}
// use case 2: has no children and parent_id is 0 , it's parent item
if (empty($child['children']) && $child['parent_id'] != "0") {
array_push($list, $child['id']);
}
// has children and parent_id is not 0 , recall function to treat use case 1 and 2 ..
findParentIds($child['children'], $list);
}
}
Below my Laravel controller code
public function templateValueData($template_id)
{
$parent = TemplateMetadataValue::where('template_id', $template_id)->get();
return $parent;
}
Here, the output of the method above;
[
{
"id": 11,
"parent_id": 0,
"name": "Category 1"
},
{
"id": 63,
"parent_id": 11,
"name": "Category 2"
},
{
"id": 73,
"parent_id": 63,
"name": "Category 3"
},
{
"id": 77,
"parent_id": 11,
"name": "Category 5"
},
{
"id": 83,
"parent_id": 77,
"name": "Category 8"
}
]
Now I want to format this response base on the value of parent_id.
The condition is: when parent_id is equal to id, then the parent_id is considered as children of the id.
Please note that both parent_id and id are stored as incremental values.
I want to alter and return the response as below;
[
{
"id": 11,
"parent_id": 0,
"name": "Category 1",
"children": [
{
"id": 63,
"parent_id": 11,
"name": "Category 2",
"children": [
{
"id": 73,
"parent_id": 63,
"name": "Category 3"
}
]
},
{
"id": 77,
"parent_id": 11,
"name": "Category 5",
"children": [
{
"id": 83,
"parent_id": 77,
"name": "Category 8"
}
]
}
]
}
]
How can I achieve this?
Use below code for achieve your response.
public function templateValueData($template_id)
{
$parents = TemplateMetadataValue::where('template_id', $template_id)->get();
$arr = $this->xyz($parents);
return $arr;
}
Function for achieve response.
function xyz($parents,$parent_id = 0){
$res = [];
foreach ( $parents as $parent ) {
if ( $parent[ "parent_id"] == $parent_id ){
$children = $this->xyz($parents, $parent[ "id"]);
if ($children) {
$parent[ "children"] = $children;
}
$res[]=$parent;
}
}
return $res;
}
You should call your relation on your controller:
public function templateValueData($template_id)
{
$parent = TemplateMetadataValue::where('template_id',$template_id)->with('children')->get();
return $parent;
}
Also you can use method injection on your controller methods, so you can use this code:
public function templateValueData(TemplateMetadataValue $template)
{
return $template->load('children')->get();
}
If you slightly modify your TemplateMetadataValue model then you could achieve the desired result.
class TemplateMetadataValue extends Model {
public function children() {
return $this->hasMany(static::class, 'parent_id');
}
}
Call it with children method.
$parent = TemplateMetadataValue::with('children')->get();
Rest of the login you can apply here with your query builder.
my database table structure
this my purchase model
class Purchase extends Model
{
public function supplierDetails(){
return $this->belongsTo('App\Supplier', 'supplier_id');
}
public function purchasedItems(){
return $this->hasMany('App\PurchasedItem', 'purchase_id');
}
}
this my controller method
public function getReport(){
$result = Purchase::with('supplierDetails', 'purchasedItems')->get();
return $result;
}
this is my json result
[
{
"id": 74,
"supplier_id": 3,
"invoice_no": "wds2",
"supplier_details": {
"id": 3,
"name": "alexy"
},
"purchased_items": [
{
"id": 114,
"purchase_id": 74,
"item_id": 2
},
{
"id": 115,
"purchase_id": 74,
"item_id": 3
}
]
}
]
i can make relationship between these Three models 'Supplier', 'Purchase', and 'PurchaseItem' but cannot make relationship with 'Item' models
i want json response like this
[
{
"id": 74,
"supplier_id": 3,
"invoice_no": "wds2",
"supplier_details": {
"id": 3,
"name": "alexy"
},
"purchased_items": [
{
"id": 114,
"purchase_id": 74,
"item_id": 2,
"item_details": {
"id": 2,
"name": "item1"
}
},
{
"id": 115,
"purchase_id": 74,
"item_id": 3,
"item_details": {
"id": 3,
"name": "item2"
}
}
]
}
]
Any idea?
This might help, i haven't tested but you should and let me know.
public function getReport(){
$purchases = Purchase::all;
$result;
foreach($purchases as $p){
$itemsPurchased = $p->purchasedItems;
foreach($itemsPurchased as $pitem){
$pitem['item_details'] = $pitem->YOUR.FUNCTION.NAME.FOR.ITEM.DETAILS.
}
$p['purchased_items'] = $itemsPurchased;
$p['supplier_details'] = $p->supplierDetails;
$result = array($p);
}
return $result;
}
i made some changes and the below code works..
public function getAll(){
$array = Purchase::with('supplierDetails', 'purchasedItems')->get();
$purchases = json_decode($array,true);
$result = array();
foreach($purchases as $p){
$itemsPurchased = $p['purchased_items'];
foreach($itemsPurchased as $pitem){
$pitem['item_details'] = PurchasedItem::with('itemDetails', 'packageDetails')->where('purchase_id', '=', $pitem['purchase_id'])->get();
}
$p['supplier_details'] = $p['supplier_details'];
$p['purchased_items'] = $pitem['item_details'];
array_push($result, array($p));
}
return $result;
//[0] added to remove []sqauare bracket
}
I want to merge two same keys in an array and get the sum of the values.
I want the same structure as it is now.Because this data needs to be converted to JSON.
This is what i get now.
{
"data": [{
"count_of_invites": 5,
"user": "Rajesh",
"id": "53"
},
{
"count_of_invites": 9,
"user": "Student",
"id": "45"
},
{
"count_of_invites": 4,
"user": "Student",
"id": "45"
}
]
}
As you can see the id 45 are repeated.As i want the result as,
Expected output
{
"data": [{
"count_of_invites": 5,
"user": "Rajesh",
"id": "53"
},
{
"count_of_invites": 13,
"user": "Student",
"id": "45"
}
]
}
As you can see the duplicate entry should be removed as well as the count_of_invites of duplicate entry should be added.
<?php
$data = [
[
'id' => 2,
'name' => 'Paul',
'count' => 4
],
[
'id' => 3,
'name' => 'Peter',
'count' => 5
],
[
'id' => 3,
'name' => 'Peter',
'count' => 7
]
];
foreach($data as $array)
$counts[$array['id']][] = $array['count'];
$counts = array_map('array_sum', $counts);
foreach($data as $k => $array)
$data[$k]['count'] = $counts[$array['id']];
$data = array_unique($data, SORT_REGULAR);
print json_encode($data, JSON_PRETTY_PRINT);
Output:
[
{
"id": 2,
"name": "Paul",
"count": 4
},
{
"id": 3,
"name": "Peter",
"count": 12
}
]
You can achieve it this way:
$ids = array();
$output = array();
foreach ($input as $value) {
if (!isset($ids[$value["id"]])) {
$ids[$value["id"]]=$count($output);
$output[]=$value;
} else {
$output[$ids[$value["id"]]]["count_of_invites"] = $value["count_of_invites"];
$output[$ids[$value["id"]]]["user"] = $value["user"];
}
}
The count method was declared as variable and i've added with addition assignment operator.
Thank You for helping.
$ids = array();
$output = array();
foreach ($response as $value) {
if (!isset($ids[$value["id"]])) {
$ids[$value["id"]] = count($output);
$output[] = $value;
}
else {
$output[$ids[$value["id"]]]["count_of_invites"] += $value["count_of_invites"];
$output[$ids[$value["id"]]]["user"] = $value["user"];
}
}
I am trying to convert following table data into nested array using PHP. I am almost done but stuck at one point.
id name parent
1 Apparel
2 Appliances
46 Apparel 1
47 Child Apparel 46
49 Child Apparel 2 47
Using this code
$cats = array(); // from database
$refs = array();
$rcats = array();
foreach ($cats as $cat) {
$thisref = &$refs[$cat['id']];
$thisref['id'] = $cat['id'];
$thisref['name'] = $cat['name'];
$thisref['leaf'] = "true";
if (!$cat['parent']) {
$rcats[$cat['id']] = &$thisref;
} else {
unset($refs[$cat['parent']]['leaf']);
$refs[$cat['parent']]['items'][$cat['id']] = &$thisref;
}
}
print_r(json_encode($rcats));
This results into following JSON.
{
"1": {
"id": "1",
"name": "Apparel",
"items": {
"46": {
"id": "46",
"name": "Apparel",
"items": {
"47": {
"id": "47",
"name": "Child Apparel",
"items": {
"49": {
"id": "49",
"name": "Child Apparel 2",
"leaf": "true"
}
}
}
}
}
}
},
"2": {
"id": "2",
"name": "Appliances",
"leaf": "true"
}
}
Where as I want the JSON like
[
{
"id": "1",
"name": "Apparel",
"items": [
{
"id": "46",
"name": "Apparel",
"items": [
{
"id": "47",
"name": "Child Apparel",
"items": [
{
"id": "49",
"name": "Child Apparel 2",
"leaf": "true"
}
]
}
]
}
]
},
{
"id": "2",
"name": "Appliances",
"leaf": "true"
}
]
Am i wrong? :p
$cats = array(); // From Database, but i use this
$cats = array(
array(
'id' => 1,
'name' => 'Jakarta',
'parent' => NULL
),
array(
'id' => 2,
'name' => 'Bandung',
'parent' => 1
),
array(
'id' => 3,
'name' => 'Surabaya',
'parent' => 1
),
array(
'id' => 4,
'name' => 'Bali',
'parent' => NULL
),
array(
'id' => 5,
'name' => 'Batam',
'parent' => NULL
),
);
$refs = array();
$rcats = array();
foreach ($cats as $cat) {
$thisref = &$refs[$cat['id']];
$thisref['id'] = $cat['id'];
$thisref['name'] = $cat['name'];
$thisref['leaf'] = "true";
if (!$cat['parent']) {
$rcats[] = &$thisref;
}
else {
unset($refs[$cat['parent']]['leaf']);
$refs[$cat['parent']]['items'][] = &$thisref;
}
}
/*
// IF YOU NEED CHANGE TO OBJECT
$rcats = (object)$rcats;
if(isset($rcats->items)){
$rcats->items = (object)$rcats->items;
}
*/
header('Content-type: application/json');
print_r(json_encode($rcats));
Here I'm assuming you are storing your final results in $refs
print_r(json_encode(array_values($rcats)));
If you want to take out all the indexes, in your for loop, do [] instead of the id:
if (!$cat['parent']) {
$rcats[] = &$thisref;
} else {
unset($refs[$cat['parent']]['leaf']);
$refs[$cat['parent']]['items'][] = &$thisref;
}
A quick example I throw:
<?php
$a = array('1' => array('a' => 3), '2' => array('b' => 4));
echo json_encode($a) . "\n";
echo json_encode(array_values($a)) . "\n";
Outputs:
{"1":{"a":3},"2":{"b":4}}
[{"a":3},{"b":4}]