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.
Related
I have a comments table, with theses fields: id, body, and parent_id. comments can have subcomments, that's why it is using the parent_id field, I would like to know how I can achieve this nested result:
[
{
"id": 1,
"body": "test",
"parent_id": null,
"comments": [
{
"id": 2,
"body": "test",
"parent_id": 1,
"comments": [
{
"id": 3,
"body": "test",
"parent_id": 2
},
{
"id": 4,
"body": "test",
"parent_id": 2
}
]
},
{
"id": 5,
"body": "test",
"parent_id": 1,
"comments": []
}
]
},
{
"id": 6,
"body": "test",
"parent_id": null,
"comments": []
}
]
Without using eloquent eager loading(with()), just using the query builder, thank you.
Since the possible answer could be too long, I am posting it directly as an answer. You could achieve this by creating a recursive method in your controller like below:
public function commentsLoop($comments, $parent = null)
{
$result = [];
foreach ($comments as $comment) {
if ($comment['parent_id'] === $parent) {
$subComment = commentsLoop($comments, $comment['id']);
if ($subComment) {
$comment['comments'] = $subComment;
}
$result[] = $comment;
}
}
return $result;
}
And then, you can call it from your main method in your controller like below:
public function comments()
{
$comments = DB::table('comments')->all()->toArray();
return $this->commentsLoop($comments);
}
However, if you would use eloquent instead, creating a relationship to self in your model would be the answer.
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);
}
}
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);
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 have the following collections:
User
{
"id": 1,
"name": "Bob"
},
{
"id": 2,
"name": "Mary"
}
Stuff
{
"id": 1,
"user_id": 1,
"name": "Bob's stuff"
},
{
"id": 2,
"user_id": 2,
"name": "Mary's stuff"
},
{
"id": 3,
"user_id": 0,
"name": "Everyone's stuff"
},
{
"id": 4,
"user_id": null,
"name": "Global stuff"
}
... and the following models:
class User extends Eloquent {
public function stuff() {
return $this -> hasMany( 'Stuff' ) ;
}
}
class Stuff extends Eloquent {
public function user() {
return $this -> belongsTo( 'User' ) ;
}
}
How to I automatically add Everyone's stuff ( User.id == 0 ) and Global stuff ( User.id == null ) to everybody's model? In other words, if I was querying for Bob's stuff I wish to get the following:
{
"id": 1,
"user_id": 1,
"name": "Bob's stuff"
},
{
"id": 3,
"user_id": 0,
"name": "Everyone's stuff"
},
{
"id": 4,
"user_id": null,
"name": "Global stuff"
}
You can use this:
public function stuff() {
return $this->hasMany(Stuff::class)->orWhere('user_id', 0)->orWhereNull('user_id');
}
A disadvantage is that you can't just add other WHERE clauses:
$user->stuff()->where('foo', 'bar')->get(); // Doesn't work as expected.
As a consequence, eager loading doesn't work.