How to modify nested Laravel collection using group by - php

I want to modify nested collection using group by.
This is sample collection
"document": [
{
"id": 1,
"company_id": 4,
"client_id": 1,
"status": 1,
"client": {
"id": 1,
"company_id": 4,
"name": "1663159185735-client"
},
"document_items": [
{
"id": 1,
"master_id": 5,
"text_value": "piyo",
},
{
"id": 2,
"master_id": 5,
"text_value": "fuga",
},
{
"id": 3,
"master_id": 3,
"text_value": "hoge",
}
]
}
]
I want to change like this.
"document": [
{
"id": 1,
"company_id": 4,
"client_id": 1,
"status": 1,
"client": {
"id": 1,
"company_id": 4,
"name": "1663159185735-client"
},
"document_items": [
5: [{
"id": 1,
"master_id": 5,
"text_value": "piyo",
},
{
"id": 2,
"master_id": 5,
"text_value": "fuga",
}
],
3: [{
"id": 2,
"master_id": 5,
"text_value": "fuga",
       }
     ]
]
}
]
I try write below code;
$result->map(function ($v){
$v->documentItems = $v->documentItems->groupBy('master_id');
return $v;
});
but output key is documentItems not document_items
I changed to
$v->document_items = $v->documentItems->groupBy('master_id');
key is drawing_drawing_items but not groupby(simple array)
how to modify group by and preserve key case?

I changed to
$v->document_items = $v->documentItems->groupBy('master_id');
key is drawing_drawing_items but not groupby(simple array)
You should also change like this $v->document_items->groupBy('master_id')
The key is 'document_items' not 'documentItems'

Related

Need to find maximum level where child is present in a parent child relationship

Here is the JSON object, so if I look at below object it has children till level 3 and object at level 3 has no children's.
So what is the efficient way to find out?
{
"id": 2,
"name": "Economic Segment",
"maxLevel": 10,
"parentId": null,
"children": [
{
"id": 7,
"name": "Revenue",
"maxLevel": 9,
"parentId": 2,
"children": [
{
"id": 12,
"name": "R Child",
"maxLevel": 8,
"parentId": 7,
"children": [
{
"id": 12,
"name": "R Child",
"maxLevel": 8,
"parentId": 7,
"children": []
}
]
}
]
},
{
"id": 8,
"name": "Expenditure",
"maxLevel": 9,
"parentId": 2,
"children": []
},
{
"id": 9,
"name": "Asset",
"maxLevel": 9,
"parentId": 2,
"children": []
}
]
}}

Laravel pass parameter to its distant relationship (translate children)

I have categories with id, parent_id, slug , pivot table category_language which columns are id,category_id,language_id,value
As you can see I can translate parent category, but can't send desired $lang_id to children translations, so each children having all translations
here is what I get:
{
"id": 1,
"parent_id": 0,
"slug": "personal-computers",
"created_at": "2019-12-27 15:05:31",
"updated_at": "2019-12-27 15:05:31",
"children": [
{
"id": 3,
"parent_id": 1,
"slug": "accessories-for-pc",
"created_at": "2019-12-27 15:05:32",
"updated_at": "2019-12-27 15:05:32",
"translations": [
{
"id": 1,
"code": "en",
"name": "English",
"pivot": {
"category_id": 3,
"language_id": 1,
"value": "Acc for PC",
"id": 7
}
},
{
"id": 2,
"code": "ru",
"name": "Русский",
"pivot": {
"category_id": 3,
"language_id": 2,
"value": "Аксессуары для ноутбуков и ПК",
"id": 8
}
},
{
"id": 3,
"code": "ro",
"name": "Romana",
"pivot": {
"category_id": 3,
"language_id": 3,
"value": "aksessuari-dlya-noutbukov-i-pk-ro",
"id": 9
}
}
]
}
],
"translations": [
{
"id": 1,
"code": "en",
"name": "English",
"pivot": {
"category_id": 1,
"language_id": 1,
"value": "PC",
"id": 1
}
}
]
}
Controller:
return Category::with('children')
->with(array('translations'=>function($query) use ($lang_id){
$query->where('language_id',$lang_id);
}))
->where('parent_id',0)->first();
Model
class Category extends Model
{ ..
public function translations()
{
return $this->belongsToMany('App\Models\Translation','category_language', 'category_id' ,'language_id' )->withPivot('value','id');
}
public function children()
{
return $this->hasMany( 'App\Models\Category' , 'parent_id' , 'id' )->with('translations');
}
}
you can add condition in children method
public function children()
{
return $this->hasMany( 'App\Models\Category' , 'parent_id' , 'id' )->with('translations')->where('language_id', 1);
}
Dry7 answer was close to the one I've implemented later, so I upvoted him.
Finally in model I've added: ...->where('language_id',helper_SetCorrectLangIdForQuery());
and function helper_SetCorrectLangIdForQuery is using global helper of Laravel request()->lang . If lang=enz, than it takes default language from another helper.

Laravel make data unique but remove first duplicate

I got data from db:
$message = Message::where('user_id', $user->id)->get();
Result:
[
{
"id": 1,
"post_id": 3,
"agent_id": 1,
"user_id": 1,
"message": "hello",
"status": 0
},
{
"id": 2,
"post_id": 3,
"agent_id": 1,
"user_id": 1,
"sender_id": 1,
"message": "how are you?",
"status": 0
},
{
"id": 3,
"post_id": 3,
"agent_id": 1,
"user_id": 1,
"message": "Ill call you",
"status": 0
},
{
"id": 4,
"post_id": 5,
"agent_id": 1,
"user_id": 2,
"message": "hi hello",
"status": 0
}
]
Well, I able to make it unique and remove duplicate by user_id with:
$message->unique('user_id')
It return right result, but what I want is, remove first item not last, current result:
{
"0": {
"id": 1,
"post_id": 3,
"agent_id": 1,
"user_id": 1,
"message": "hello",
"status": 0
},
"3": {
"id": 4,
"post_id": 5,
"agent_id": 1,
"user_id": 2,
"message": "hi hello",
"status": 0
}
}
As you see, it keep first item and then removed next duplicate, in this case I want to keep id 3 and 4 also it made my object to array, I don't want this, how can I solve this?
But I want this result:
[
{
"id": 3,
"post_id": 3,
"agent_id": 1,
"user_id": 1,
"message": "Ill call you",
"status": 0
},
{
"id": 4,
"post_id": 5,
"agent_id": 1,
"user_id": 2,
"message": "hi hello",
"status": 0
}
]
To get the newest messages try sorting on id in a descending order
$messages = Message::where('user_id', $user->id)->orderBy('id', 'DESC')->get();
and you are receiving a Collection you call toArray on it to get back an array:
return $messages->unique('user_id')->toArray();
Use sorting before unique.
$message = Message::where('user_id', $user->id)->orderBy('id','desc')->get();
$message->unique('user_id');

Laravel 5.3 - Merge array based on 2 matching keys?

Given the following two arrays, how can they be merged efficiently to result in the third array?
productData
$productData =
[
{
"product_id": 4,
"type": "electronic",
"name": "monitor",
"specs": {
"HDMI": true,
"VGA": false
}
},
{
"product_id": 5,
"type": "electronic",
"name": "HDMI cable",
"specs": {
"length": "3ft"
}
},
{
"product_id": 6,
"type": "kitchen",
"name": "spoon"
}
]
products
$products =
{
"products": 3,
"per_page": 10,
"current_page": 1,
"data": [
{
"id": 4,
"product_type": "electronic",
"product_id": 6
},
{
"id": 6,
"type": "electronic",
"product_id": 5
},
{
"id": 9,
"type": "kitchen",
"product_id": 4
}
]
}
productsFinal ($productData merged into $products - based on matching combo of product_id/product_id and type/product_type)
$productsFinal =
{
"products": 3,
"per_page": 10,
"current_page": 1,
"data": [
{
"id": 4,
"product_type": "electronic",
"product_id": 6,
// How to merge product data and wrap with "data" key
"data": {
"product_id": 6,
"type": "kitchen",
"name": "spoon"
}
},
{
"id": 6,
"type": "electronic",
"product_id": 5,
// How to merge product data and wrap in "data" key
"data": {
"product_id": 5,
"type": "electronic",
"name": "HDMI cable",
"specs": {
"length": "3ft"
}
}
},
{
"id": 9,
"type": "kitchen",
"product_id": 4,
// How to merge product data and wrap in "data" key
"data": {
"product_id": 6,
"type": "kitchen",
"name": "spoon"
}
}
]
}
I tried different things for the outcome in a foreach loop but still cannot get it to render as intended:
foreach($productData as $productDataItem) {
// when $productDataItem.product_id == $product.product_id && $productDataItem.type == $product.product_type
// move the matching $productDataItem object into matching $product object, wrapped in a new "data" key
}
I don't know Laravel too well. However you can join your data objects quite easily:
<?php
$productData = json_decode('[
{
"product_id": 4,
"type": "electronic",
"name": "monitor",
"specs": {
"HDMI": true,
"VGA": false
}
},
{
"product_id": 5,
"type": "electronic",
"name": "HDMI cable",
"specs": {
"length": "3ft"
}
},
{
"product_id": 6,
"type": "kitchen",
"name": "spoon"
}
]');
$products = json_decode('{
"products": 3,
"per_page": 10,
"current_page": 1,
"data": [
{
"id": 4,
"type": "electronic",
"product_id": 6
},
{
"id": 6,
"type": "electronic",
"product_id": 5
},
{
"id": 9,
"type": "kitchen",
"product_id": 4
}
]
}');
// combine both data objects
foreach($products->data As &$p) {
foreach($productData As $d) {
if(property_exists($p, "product_id") && property_exists($d, "product_id") && property_exists($p, "type") && property_exists($d, "type")) {
if($p->product_id==$d->product_id && $p->type==$d->type) {
//$p = (object) array_merge((array) $p, (array) $d);
$p->data = $d; // updated answer
continue;
}
}
}
}
echo("<pre>");
echo json_encode($products, JSON_PRETTY_PRINT);
?>
You can test the code here: http://sandbox.onlinephpfunctions.com/code/98a50c35ee32c30f0d2be1661f7afb5895174cbe
Update: http://sandbox.onlinephpfunctions.com/code/aeebfdcf4f4db5e960260e931982570cfed19e0e
I would suggest to check this package dingo/api. I assume you want to display some kind of JSON response. Take a look at Transformers. You can do something like this :
<?php
namespace App\Http\Transformers;
use App\Http\Controllers\ProductData;
use League\Fractal\TransformerAbstract;
class ProductsDataTransformer extends TransformerAbstract
{
/**
* Turn this item object into a generic array
*
* #return array
*/
public function transform(ProductData $productdata)
{
return [
'id' => $productdata->id,
'product_type' => $productdata->product_type,
'product /*or data*/' => Product::find($productdata->product_id),
];
}
}
This would find the product by it's ID and look like this :
{
"id": 4,
"product_type": "electronic",
"product" {
"product_id": 6,
"type": "kitchen",
"name": "spoon"
},
},
You can then also create a transformer for Product to take care of your specs attribute to do the same thing.

Getting unique results with elasticsearch acorrding to field

I'm using FOSElasticaBundle with Symfony2 on my project and there are entry and user tables on MySQL database and each entry belongs to one user.
I want to get just one entry per a user among the whole entries from the database.
Entries Representation
[
{
"id": 1,
"name": "Hello world",
"user": {
"id": 17,
"username": "foo"
}
},
{
"id": 2,
"name": "Lorem ipsum",
"user": {
"id": 15,
"username": "bar"
}
},
{
"id": 3,
"name": "Dolar sit amet",
"user": {
"id": 17,
"username": "foo"
}
},
]
Expected result is:
[
{
"id": 1,
"name": "Hello world",
"user": {
"id": 17,
"username": "foo"
}
},
{
"id": 2,
"name": "Lorem ipsum",
"user": {
"id": 15,
"username": "bar"
}
}
]
But it returns all entries on table. I've tried to add an aggregation to my elasticsearch query and nothing changed.
$distinctAgg = new \Elastica\Aggregation\Terms("distinctAgg");
$distinctAgg->setField("user.id");
$distinctAgg->setSize(1);
$query->addAggregation($distinctAgg);
Is there any way to do this via term filter or anything else? Any help would be great. Thank you.
Aggregations are not easy to understand when you are used to MySQL group by.
The first thing, is that aggregations results are not returned in hits, but in aggregations. So when you get the result of your search, you have to get aggregations like that :
$results = $search->search();
$aggregationsResults = $results->getAggregations();
The second thing is that aggregations wont return you the source. With the aggregation of your example, you will only know that you have 1 user with ID 15, and 2 users with ID 15.
E.g. with this query :
{
"query": {
"match_all": {}
},
"aggs": {
"byUser": {
"terms": {
"field": "user.id"
}
}
}
}
Result:
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 1,
"hits": [ ... ]
},
"aggregations": {
"byUser": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": 17,
"doc_count": 2
},
{
"key": 15,
"doc_count": 1
}
]
}
}
}
If you want to get results, the same way you would do with a GROUP BY in MySQL, you have to use a top_hits sub-aggregation:
{
"query": {
"match_all": {}
},
"aggs": {
"byUser": {
"terms": {
"field": "user.id"
},
"aggs": {
"results": {
"top_hits": {
"size": 1
}
}
}
}
}
}
Result:
{
"took": 3,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 1,
"hits": [ ... ]
},
"aggregations": {
"byUser": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": 17,
"doc_count": 2,
"results": {
"hits": {
"total": 2,
"max_score": 1,
"hits": [
{
"_index": "test_stackoverflow",
"_type": "test1",
"_id": "1",
"_score": 1,
"_source": {
"id": 1,
"name": "Hello world",
"user": {
"id": 17,
"username": "foo"
}
}
}
]
}
}
},
{
"key": 15,
"doc_count": 1,
"results": {
"hits": {
"total": 1,
"max_score": 1,
"hits": [
{
"_index": "test_stackoverflow",
"_type": "test1",
"_id": "2",
"_score": 1,
"_source": {
"id": 2,
"name": "Lorem ipsum",
"user": {
"id": 15,
"username": "bar"
}
}
}
]
}
}
}
]
}
}
}
More informations on this page : https://www.elastic.co/blog/top-hits-aggregation

Categories