I'm trying to sort nested collection but i can't get correct results.
category tags collection
[
{
"id": 1,
"name": "Age",
"tags": [
{
"id": 1,
"name": "18+"
},
{
"id": 2,
"name": "21+"
},
{
"id": 3,
"name": "25+"
},
{
"id": 4,
"name": "30+"
},
{
"id": 5,
"name": "17+"
},
{
"id": 6,
"name": "16+"
},
{
"id": 7,
"name": "26+"
},
{
"id": 8,
"name": "13+"
},
{
"id": 9,
"name": "24+"
},
{
"id": 10,
"name": "20+"
},
{
"id": 11,
"name": "19+"
}
],
}
]
i'm trying to sort tags:
<?php
$categoryTagsSorted = $categoryTags->transform(
function ($categoryTag) {
$sortedTags = $categoryTag->tags->sortBy('name');
// $sortedTags->values() return correct sorted array
$categoryTag->tags = $sortedTags->values();// < - this not work
return $categoryTag;
}
);
$categoryTagsSorted returns untouched results, but when i assign $sortedTags
to $categoryTag->newField = $sortedTags->values() i've got sorted tags in newField.
Question is how to return collection with sorted tags?
you can try this :
$categoryTagsSorted = $categoryTags->each(
function ($categoryTag) {
$categoryTag = $categoryTag->tags->sortBy('name');
}
);
In your code I believe you have misunderstood the use of the transform() method. It is used to iterate over each item in the collection and replaces the item with the value you give it in the callback. In your code you have also simply returned the original value without applying any 'transformation' to it. If you wish to alter the value of tags on the object you could try reassigning the object property:
$categoryTags->tags = $categoryTags->tags->sortBy('name');
Read the docs on the transform method here: https://laravel.com/docs/9.x/collections#method-transform#
Update
I think you can simply use map and re-assign the tags property to accomplish the solution:
$categoryTags->map(function($categoryTag) {
$categoryTag->tags = $categoryTag->tags->sortBy('name');
return $categoryTag;
});
The following test code worked OK for me...
$categoryTags = collect([
(object) [
'id' => 1,
'name' => 'Age',
'tags' => collect([
['id' => 1, 'name' => '18+'],
['id' => 2, 'name' => '21+'],
['id' => 3, 'name' => "25+"],
['id' => 4, 'name' => "30+"],
['id' => 5, 'name' => "17+"]
])
]
]);
$categoryTags->map(function($categoryTag) {
$categoryTag->tags = $categoryTag->tags->sortBy('name');
return $categoryTag;
});
var_dump($categoryTags);
Output:
object(Illuminate\Support\Collection)#1029 (1) {
["items":protected]=>
array(1) {
[0]=>
object(stdClass)#1030 (3) {
["id"]=>
int(1)
["name"]=>
string(3) "Age"
["tags"]=>
object(Illuminate\Support\Collection)#1026 (1) {
["items":protected]=>
array(5) {
[4]=>
array(2) {
["id"]=>
int(5)
["name"]=>
string(3) "17+"
}
[0]=>
array(2) {
["id"]=>
int(1)
["name"]=>
string(3) "18+"
}
[1]=>
array(2) {
["id"]=>
int(2)
["name"]=>
string(3) "21+"
}
[2]=>
array(2) {
["id"]=>
int(3)
["name"]=>
string(3) "25+"
}
[3]=>
array(2) {
["id"]=>
int(4)
["name"]=>
string(3) "30+"
}
}
}
}
}
}
=> null
Try it out here: https://web.tinkerwell.app/#/snippets/e2c93c20-5706-4ffd-bcc0-601aae861c8b (takes a min to load)
Related
i have a problem for the response, i want to change the response API because i need for my mobile APP, the feature have filter object based on date. So i hope you all can help me to solve the problem
i wanna change the response for my API
before:
{
"tasks": [
{
"id": 7,
"user_id": 1,
"title": "gh",
"date": "2022-02-10 13:05:00",
"deskripsi": "gfh",
"created_at": "2022-02-09T06:05:56.000000Z",
"updated_at": "2022-02-09T06:05:56.000000Z"
},
{
"id": 5,
"user_id": 1,
"title": "ghf",
"date": "2022-02-17 16:05:00",
"deskripsi": "fghf",
"created_at": "2022-02-09T06:05:12.000000Z",
"updated_at": "2022-02-09T06:05:12.000000Z"
},
{
"id": 6,
"user_id": 1,
"title": "fgh",
"date": "2022-02-17 18:05:00",
"deskripsi": "gh",
"created_at": "2022-02-09T06:05:40.000000Z",
"updated_at": "2022-02-09T06:05:40.000000Z"
}
]
}
here is the code for the response API above
return response([
'tasks' => Task::where('user_id', auth()->user()->id)->where('date','>',NOW())->orderBy('date','asc')->get(),
],200);
and i want to change it my response API into this response
{
"tasks": [
{
"date": "2022-02-10",
"task": [
{
"id": 7,
"user_id": 1,
"title": "gh",
"date": "2022-02-10 13:05:00",
"deskripsi": "gfh",
"created_at": "2022-02-09T06:05:56.000000Z",
"updated_at": "2022-02-09T06:05:56.000000Z"
},
{
"id": 7,
"user_id": 1,
"title": "gh",
"date": "2022-02-10 15:05:00",
"deskripsi": "gfh",
"created_at": "2022-02-09T06:05:56.000000Z",
"updated_at": "2022-02-09T06:05:56.000000Z"
}
]
},
{
"date": "2022-02-12",
"task": [
{
"id": 7,
"user_id": 1,
"title": "gh",
"date": "2022-02-12 13:05:00",
"deskripsi": "gfh",
"created_at": "2022-02-09T06:05:56.000000Z",
"updated_at": "2022-02-09T06:05:56.000000Z"
}
]
},
]
}
Do groupBy on the resulting Collection from the query (see docs: https://laravel.com/docs/9.x/collections#method-groupby)
For example, you could do:
$tasksGroupedByDate = Task::where(.......)
->get()
->groupBy(fn (Task $task) => $task->date->format('Y-m-d'));
(Note: above uses PHP 7.4 arrow functions. Also, add a date cast on the date column in your Task model to be able to use ->format( directly on the date field)
The above code results to:
{
'2022-01-01' => [
{ Task object },
{ Task object },
{ Task object },
],
'2022-01-02' => [
{ Task object },
{ Task object },
{ Task object },
],
}
(used Task object for brevity, but that will be ['id' => 1, 'title' => 'Task name', .....])
To morph that to the structure you want, you can use map and then values to remove the keys and turn it back to an ordered array:
$tasksGroupedByDate->map(fn ($tasksInGroup, $date) => [
'date' => $date,
'task' => $tasksInGroup
])->values();
If you want to combine everything into one method chain:
return [
'tasks' => Task::where(......)
->get()
->groupBy(fn (Task $task) => $task->date->format('Y-m-d'))
->map(fn ($tasksInGroup, $date) => [
'date' => $date,
'task' => $tasksInGroup
])
->values(),
];
It sounds like you want to create a human friendly date field based on the date column, then group by it.
While solutions do exists to accomplish this at the database level, I believe you'd still need to loop around it again afterwards to get the hierarchy structure you're looking for. I don't think it's too complicated for PHP to loop through it.
My suggestion is as follows:
Before:
return response([
'tasks' => Task::where('user_id', auth()->user()->id)
->where('date','>',NOW())->orderBy('date','asc')->get(),
],200);
After:
$out = [];
$tasks = Task::where('user_id', auth()->user()->id)
->where('date','>',NOW())->orderBy('date','asc')->get();
foreach($tasks as $task) {
$date = strtok((string)$task->date, ' ');
if (empty($out[$date])) {
$out[$date] = (object)['date' => $date, 'task' => []];
}
$out[$date]->task[] = $task;
}
$out = array_values($out);
return response(['tasks' => $out], 200);
Note in the above I'm using the function strtok. This function might look new even to the most senior of php developers.... It's a lot like explode, except it can be used to grab only the first part before the token you're splitting on. While I could have used explode, since the latter part after the token isn't needed, strtok is better suited for the job here.
$task = Task::where('user_id', auth()->user()->id)->where('date','>',NOW())->orderBy('date','asc')->get();
foreach($task as $item){
$date[] = item->date;
$result = Task::where('user_id', auth()->user()->id)->where('date','=', $date)->get();
}
return response([
'tasks' =>
['date' => $date,
'task' => $task]
],200);
maybe something like this
I have a collection:
{
"2020-09-06 12:00:00": [
{
"usage_point_id": "12345678912345",
"key": "0.7680189"
},
{
"usage_point_id": "12345678912346",
"key": "0.29539188"
},
]
}
I would like to transform it to:
[{
"timestamp": "2019-12-18T10:30:00Z",
"keys": [
{
"usage_point_id": "12345678912345",
"key": "12,5"
},
{
"usage_point_id": "12345678912346",
"key": "0.29539188"
},
]
}]
They question is how to change:
"2020-09-06 12:00:00": [
with
"timestamp": "2019-12-18T10:30:00Z",
I get this collection with the query:
$measures = Measure::where('operation_id', $operation->name)
->whereBetween('time', [$from, $to])
->select(['time AS timestamp', 'meter_id AS usage_point_id', 'repartition_rate AS key'])
->get()->groupBy('timestamp');
is there a special helper for collections for that purpose ?
The easiest solution to your problem would be to map through all entries and modify their contents:
$measures
->map(function ($item, $key) {
return [
"timestamp" => Carbon\Carbon::make($key)->toISOString(),
"keys" => $item,
];
})
->values();
In Juliatzin case it constantly overwrites the key so it only returns the last entry if you have multiple entries from your database result.
I got the solution, with mapWithKeys
$measures = $measures->mapWithKeys(function ($measure, $key) {
return [
'timestamp' => $key,
'keys' => $measure
];
});
Hiii
I have 2 database tables with the columns table :1 "id, invoice_id, subject, total" table:2 "id, invoice_id, item_name, price".whenever i try to update record with the help of invoice_id if record doesn't exist in item table it will not insert new item in item table.
here i attached my JSON data
{
"date": "2019-06-08",
"client_id": "1",
"currency_id": 4,
"total_amount": null,
"subject": "RD Management",
"items": [
{
"item_name": "Saving",
"price": "500"
},
{
"item_name": "Fix",
"price": "500"
},
{
item_name": "Current",
"price": "200"
}
]
}
here one problem is also
my JSON can not send item_id also
so without item id how can i update my record...???
here 3rd item is not present in my table
here is my controller
foreach ($request->items as $key => $items)
{
$item_update = [
'item_name' => $items['item_name'],
'price' => $items['price']
];
DB::table('items')
->where('invoice_id', $id)
->update($item_update);
}
I Except output like this
"items": [
{
"id": 1,
"invoice_id": "1",
"item_name": "Saving",
"price": "500",
},
{
"id": 2,
"invoice_id": "1",
"item_name": "Fix",
"price": "500",
},
{
"id": 3,
"invoice_id": "1",
"item_name": "current",
"price": "200",
},
]
but my actual output is
"items":[
{
"id":"1"
"item_name": "Fix",
"price": "500",
},
{
"id":"2"
"invoice_id": "1",
"item_name": "Fix",
"price": "500",
}
]
this output override item_name at update time.
there are any way to solve this both problem.
If you can't identify which items already exist and which ones are new, your remaining option is to identify items by item_name+invoice_id. The downside is that you cannot update item_name this way.
If you have Eloquent models properly set up, you can use updateOrCreate().
<?php
foreach ($request->items as $key => $items)
{
$itemAfterUpdate = App\Item::updateOrCreate(
[
'invoice_id' => $id,
'item_name' => $items['item_name']
],
[ 'price' => $items['price'] ]
);
}
If not, you will basically have to do what Eloquent does behind the scenes, which is check if the item already exists based on item_name and invoice_id, and then insert or update accordingly.
<?php
foreach ($request->items as $key => $items)
{
$alreadyExists = DB::table('items')
->where('invoice_id', $id)
->where('item_name', $items['item_name'])
->exists();
}
if($alreadyExists){
DB::table('items')
->where('invoice_id', $id)
->where('item_name' => $items['item_name'])
->update(['price' => $items['price']);
}
else{
DB::table('items')->insert([
'invoice_id' => $id,
'item_name' => $items['item_name'],
'price' => $items['price']
]);
}
}
I don't understand why this database test fails. I'm aware that i don't assert on the created_at and updated_at columns, but the three columns (id, user_id, thing_id) should be enough and i'm sure that i have tested on just a selection of columns before, and it has worked!
What am i missing?
Failed asserting that a row in the table [thing_history] matches the attributes [
{
"id": 1,
"user_id": 1,
"thing_id": 1
},
{
"id": 2,
"user_id": 1,
"thing_id": 2
},
{
"id": 3,
"user_id": 1,
"thing_id": 3
}
].
Found: [
{
"id": "1",
"user_id": "1",
"thing_id": "1",
"created_at": "2019-02-01 21:18:17",
"updated_at": "2019-02-01 21:18:17"
},
{
"id": "2",
"user_id": "1",
"thing_id": "2",
"created_at": "2019-02-01 21:18:17",
"updated_at": "2019-02-01 21:18:17"
},
{
"id": "3",
"user_id": "1",
"thing_id": "3",
"created_at": "2019-02-01 21:18:17",
"updated_at": "2019-02-01 21:18:17"
}
]
This is the test code
/** #test */
public function retrieving_feed_creates_history()
{
$user = factory('App\User')->create();
$this->actingAs($user);
factory('App\Thing', 3)->create();
$response = $this->json('GET', '/api/thing/feed/all');
$this->assertDatabaseHas('feed_histories', [
[
'id' => 1,
'thing_id' => 1,
'user_id' => $user->id,
],
[
'id' => 2,
'thing_id' => 2,
'user_id' => $user->id,
],
[
'id' => 3,
'thing_id' => 3,
'user_id' => $user->id,
]
]);
}
This is the migration code:
public function up()
{
Schema::create('feed_histories', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id');
$table->integer('thing_id');
$table->timestamps();
});
}
Seems like i have misunderstood something. To check several rows, i have to split the test into into separate assertions for each row.
This works fine:
$this->assertDatabaseHas('feed_histories', [
'thing_id' => $thingA->id,
'user_id' => $user->id,
]);
$this->assertDatabaseHas('feed_histories', [
'thing_id' => $thingB->id,
'user_id' => $user->id,
]);
Yeah, the map of multiple records fails as assertDatabaseHas function currently only handles a single row by mapping a single row in the where clause...
To get a better insight, you can have a look at the base function of assertDatabaseHas
public function matches($table): bool
{
return $this->database->table($table)->where($this->data)->count() > 0;
}
here, the $this->data refers to the second argument of assertDatabaseHas funciton
So, it clears out our doubt of why we can't pass array of arrays.
Issue I am facing is I need aggregation of CSV inputs on ID, and it contains multiple nesting. I am able to perform single nesting, but on further nesting, I am not able to write correct syntax.
INPUT:
input {
generator {
id => "first"
type => 'csv'
message => '829cd0e0-8d24-4f25-92e1-724e6bd811e0,GSIH1,2017-10-10 00:00:00.000,HCC,0.83,COMMUNITYID1'
count => 1
}
generator {
id => "second"
type => 'csv'
message => '829cd0e0-8d24-4f25-92e1-724e6bd811e0,GSIH1,2017-10-10 00:00:00.000,LACE,12,COMMUNITYID1'
count => 1
}
generator {
id => "third"
type => 'csv'
message => '829cd0e0-8d24-4f25-92e1-724e6bd811e0,GSIH1,2017-10-10 00:00:00.000,CCI,0.23,COMMUNITYID1'
count => 1
}
}
filter
{
csv {
columns => ['id', 'reference', 'occurrenceDateTime', 'code', 'probabilityDecimal', 'comment']
}
mutate {
rename => {
"reference" => "[subject][reference]"
"code" => "[prediction][outcome][coding][code]"
"probabilityDecimal" => "[prediction][probabilityDecimal]"
}
}
mutate {
add_field => {
"[resourceType]" => "RiskAssessment"
"[prediction][outcome][text]" => "Member HCC score based on CMS HCC V22 risk adjustment model"
"[status]" => "final"
}
}
mutate {
update => {
"[subject][reference]" => "Patient/%{[subject][reference]}"
"[comment]" => "CommunityId/%{[comment]}"
}
}
mutate {
remove_field => [ "#timestamp", "sequence", "#version", "message", "host", "type" ]
}
}
filter {
aggregate {
task_id => "%{id}"
code => "
map['resourceType'] = event.get('resourceType')
map['id'] = event.get('id')
map['status'] = event.get('status')
map['occurrenceDateTime'] = event.get('occurrenceDateTime')
map['comment'] = event.get('comment')
map['[reference]'] = event.get('[subject][reference]')
map['[prediction]'] ||=
map['[prediction]'] << {
'code' => event.get('[prediction][outcome][coding][code]'),
'text' => event.get('[prediction][outcome][text]'),
'probabilityDecimal'=> event.get('[prediction][probabilityDecimal]')
}
event.cancel()
"
push_previous_map_as_event => true
timeout => 3
}
mutate {
remove_field => [ "#timestamp", "tags", "#version"]
}
}
output{
elasticsearch {
template => "templates/riskFactor.json"
template_name => "riskFactor"
action => "index"
hosts => ["localhost:9201"]
index => ["deepak"]
}
stdout {
codec => json{}
}
}
OUTPUT:
{
"reference": "Patient/GSIH1",
"comment": "CommunityId/COMMUNITYID1",
"id": "829cd0e0-8d24-4f25-92e1-724e6bd811e0",
"status": "final",
"resourceType": "RiskAssessment",
"occurrenceDateTime": "2017-10-10 00:00:00.000",
"prediction": [
{
"probabilityDecimal": "0.83",
"code": "HCC",
"text": "Member HCC score based on CMS HCC V22 risk adjustment model"
},
{
"probabilityDecimal": "0.23",
"code": "CCI",
"text": "Member HCC score based on CMS HCC V22 risk adjustment model"
},
{
"probabilityDecimal": "12",
"code": "LACE",
"text": "Member HCC score based on CMS HCC V22 risk adjustment model"
}
]
}
REQUIRED OUTPUT:
{
"resourceType": "RiskAssessment",
"id": "829cd0e0-8d24-4f25-92e1-724e6bd811e0",
"status": "final",
"subject": {
"reference": "Patient/GSIH1"
},
"occurrenceDateTime": "2017-10-10 00:00:00.000",
"prediction": [
{
"outcome": {
"coding": [
{
"code": "HCC"
}
],
"text": "Member HCC score based on CMS HCC V22 risk adjustment model"
},
"probabilityDecimal": 0.83
},
{
"outcome": {
"coding": [
{
"code": "CCI"
}
],
"text": "Member HCC score based on CMS HCC V22 risk adjustment model"
},
"probabilityDecimal": 0.83
}
],
"comment": "CommunityId/COMMUNITYID1"
}