Laravel reject function on nested collection - laravel

I am trying to iterate my collection, which has nested collections, with the collection reject() function and return the original collection with the updated nested data.
So this is my quiz collection
{
"id": 1,
"title": "quiz 1",
"questions": [
{
"id": 1,
"quiz_id": 1,
"question": "q1 - q1",
"answers": [
{
"id": 1,
"question_id": 1,
"answer": "answer to question 1"
},
{
"id": 2,
"question_id": 1,
"answer": "answer to question 1"
}
]
},
{
"id": 2,
"quiz_id": 1,
"question": "q1 - q2",
"answers": [
{
"id": 3,
"question_id": 2,
"answer": "answer to question 1"
}
]
}
]
}
Now I want to iterate through the questions and reject those that were already answered and exist in the result collection. I do so by using the reject() function
$quizResults = [
{
"id": 1,
"user_id": 1,
"quiz_id": 1,
"question_id": 1,
"answer_id": 1,
"correct": 1,
"created_at": null,
"updated_at": null
}
];
$filtered = $quiz->questions->reject(function ($value, $key) use ($quizResult) {
return $quizResult->contains('question_id', $value->id);
});
I want to return the quiz collection with the updated questions (without the rejected questions), but I can't seem to figure out the correct way to accomplish this. (if i return just the $filtered variable i get the correct filtered results, however if i try putting it back in to the $quiz var it won't apply the rejected filter). If I just do
return $quiz
I will get the same original collection with all the questions as if I never ran the reject() function.
How do I make the $quiz->questions = $filtered; ?

Pass it by reference
$filtered = $quiz->questions->reject(function ($value, $key) use (&$quizResult) {
return $quizResult->contains('question_id', $value->id);
});

This is the min steps to reproduce your problem,
$quizResult = [
0 => [
'id' => 1,
'user_id' => 1,
'quiz_id' => 1,
'question_id' => 1,
'answer_id' => 1,
'correct' => 1,
'created_at' => null,
'updated_at' => null,
],
];
$quiz = [
'id' => 1,
'title' => 'quiz 1',
'questions' => [
0 => [
'id' => 1,
'quiz_id' => 1,
'question' => 'q1 - q1',
'answers' => [
0 => [
'id' => 1,
'question_id' => 1,
'answer' => 'answer to question 1',
],
1 => [
'id' => 2,
'question_id' => 1,
'answer' => 'answer to question 1',
],
],
],
1 => [
'id' => 2,
'quiz_id' => 1,
'question' => 'q1 - q2',
'answers' => [
0 => [
'id' => 3,
'question_id' => 2,
'answer' => 'answer to question 1',
],
],
],
],
];
$filtered = collect($quiz['questions'])->reject(function ($value) use ($quizResult) {
return collect($quizResult)->contains('question_id', $value['id']);
});
$quiz['questions'] = $filtered;
return $quiz;
And this the result,
{
"id": 1,
"title": "quiz 1",
"questions": {
"1": {
"id": 2,
"quiz_id": 1,
"question": "q1 - q2",
"answers": [
{
"id": 3,
"question_id": 2,
"answer": "answer to question 1"
}
]
}
}
}
I do not understand why you said it's returning original one.

I had to unset the $quiz['questions'] first so it was like this
unset($quiz['questions']);
$quiz['questions'] = $filtered->flatten();
return $quiz;
and the result was as intended:
{
"id": 1,
"title": "quiz 1",
"questions": [
{
"id": 2,
"quiz_id": 1,
"question": "q1 - q2",
"answers": [
{
"id": 3,
"question_id": 2,
"answer": "answer to question 1"
}
]
}
]
}

Related

How do I sort a Laravel Collection by an array?

I know this has been asked several times, but none of the answers work for me.
I have a collection of products; each has an eloquent relationship containing multiple items.
Here's how I'm retrieving the collection
$collections = Collection::with('items')->get();
And this is the result of return $collections from the controller.
[
{
"id": 1,
"name": "Product One",
"items": [
{
"id": 1,
"product_id": 1,
"name": "Item One",
},
{
"id": 2,
"product_id": 1,
"name": "Item Two",
},
{
"id": 3,
"product_id": 1,
"name": "Item Three",
},
},
{
"id": 2,
"name": "Product Two",
"items": [
{
"id": 1,
"product_id": 2,
"name": "Item One",
},
{
"id": 2,
"product_id": 2,
"name": "Item Two",
},
{
"id": 3,
"product_id": 2,
"name": "Item Three",
},
}
]
I'd like to sort each product's items in a different order.
I'd like to sort Product One's items as 3, 1, 2 and Product Two's items as 2, 3, 1.
So I created an array of the new sort order, and then added a callback
$newSortOrder = [
1 => [3, 1, 2],
2 => [2, 3, 1]
];
$collections = Collection::with('items')->get();
foreach ($collections as $collection) {
$collection->items->sortBy(function ($model) use ($newSortOrder) {
return array_search($model->id, $newSortOrder[$model->id]);
});
}
dd($collections); <-- this is still in the default sort order from the db
However, when I return $collections, the items are still in the default order. What am I doing wrong?
Edit: tried this as well but with same results; $collection items are returned in the default order.
foreach ($collections as $collection) {
$collection->items->sortBy(function ($model) use ($order) {
return array_search($model->getkey(), $order);
});
}
$sortOrder = [
1 => [3, 1, 2],
2 => [2, 3, 1]
];
$sorted = $products->map(function($product) use ($sortOrder) {
$order = $sortOrder[$product->id];
return [
...$product,
'items' => $product->items->mapWithKeys(
fn($item) => [array_search($item['id'], $order) => $item]
)->sortKeys()
];
});
This Outputs
Illuminate\Support\Collection {#4720
#items: array:2 [
0 => array:3 [
"id" => 1
"name" => "product one"
"items" => Illuminate\Support\Collection {#4722
#items: array:3 [
0 => array:2 [
"id" => 3
"name" => "three"
]
1 => array:2 [
"id" => 1
"name" => "one"
]
2 => array:2 [
"id" => 2
"name" => "two"
]
]
#escapeWhenCastingToString: false
}
]
1 => array:3 [
"id" => 2
"name" => "product two"
"items" => Illuminate\Support\Collection {#4721
#items: array:3 [
0 => array:2 [
"id" => 2
"name" => "two"
]
1 => array:2 [
"id" => 3
"name" => "three"
]
2 => array:2 [
"id" => 1
"name" => "one"
]
]
#escapeWhenCastingToString: false
}
]
]
#escapeWhenCastingToString: false
}
you can recreate items using map().
// not recommend because key does not support for correct item's id
// $newSortOrder = [
// 1 => [3, 1, 2],
// 2 => [2, 3, 1]
// ];
$newSortOrder = [
[
"id" => 1,
"sortOrder" => [3, 1, 2]
],
[
"id" => 2,
"sortOrder" => [2, 3, 1]
],
];
$collections = Collection::with('items')->get();
$sortedCollection = collect($collections)->map(function($item) use($newSortOrder) {
$id = data_get($item,'id');
$name = data_get($item,'name');
$originItems = data_get($item,'items');
$sortOrderList = data_get(collect($newSortOrder)->firstWhere('id',$id),'sortOrder');
$items = collect($sortOrderList)->map(function($sortNumber) use($originItems) {
return collect($originItems)->firstWhere('id',$sortNumber);
});
return compact('id','name','items');
});
dd($sortedCollection); // result

How can I add pagination in Lumen 8 collection?

I am using Lumen to create api where I integrate collection. All code details are given below. I want to add pagination according to my code. How I add paginator in my code?
In Controller:
//To get all Employee Type
public function getAllEmployeeTypes(){
$employeeType = OsEmployeeType::where('status',1)->orderBy('priority', 'DESC')->get();
return new OsEmployeeTypeCollection($employeeType);
}
In Collection it looks like
public function toArray($request)
{
return [
'data' => $this->collection->map(function($data) {
return [
'id' => $data->id,
'name' => $data->name,
'priority' => $data->priority,
'status' => $data->status,
];
})
];
}
public function with($request)
{
return [
'success' => true,
'status' => 200
];
}
and the response that I got
{
"data": [
{
"id": 3,
"name": "Type 3",
"priority": 3,
"status": 1
},
{
"id": 2,
"name": "Type 2",
"priority": 2,
"status": 1
},
{
"id": 1,
"name": "Type 1",
"priority": 1,
"status": 1
}
],
"success": true,
"status": 200
}
How can I paginate my API response?
Paginate is the same as Laravel in Lumen. You will do just the same as in Laravel.
$employeeType = OsEmployeeType::where('status',1)->orderBy('priority', 'DESC')->skip(10)->take(5)->get();

Unrecognized option to $bucket: $group

getting the above error when i run $bucket with $group in mongodb laravel.
input collection:
{
"id": "n54qllhzwdlxqvy",
"season_id": "vl7oqdehzjxr510",
"status_id": 8,
"venue_id": "",
"referee_id": "",
"neutral": 1,
"note": "",
"home_scores": [0, 0, 0, 0, 0, 0, 0],
"away_scores": [1, 0, 0, 0, 0, 0, 0],
"home_position": "",
"away_position": "",
"coverage": {
"mlive": 0,
"lineup": 0
},
"round": {
"stage_id": "ednm9whz7xoryox",
"round_num": 0,
"group_num": 3
},
"updated_at": "2021-05-03 16:53:52",
"competition_id": "gpxwrxlhdgryk0j",
"competition_name": "Chinese U18 Women's National Games",
"home_team_id": "2y8m4zh8xwjql07",
"home_team_name": "Fujian U18 Women ",
"away_team_id": "8yomo4hvxg2q0j6",
"away_team_name": "Shandong U18 Women",
"match_time": "2021-05-03 15:00:00",
"match_tsp": 1620025200,
"created_at": "2021-05-04 14:33:05"
}
$data = DB::connection('mongodb_football')->collection('match_list')->raw(function ($collection) {
return $collection->aggregate([
[
'$bucket' => [
'groupBy' => '$competition_id',
'boundaries' => ["4zp5rzgh3nq82w1","4zp5rzghjjgq82w", "4zp5rzghkzq82w1","4zp5rzghpvnq82w","4zp5rzghx38q82w","4zp5rzghx5q82w1"],
'default' => "Other",
'output' => [
"data" => [
'$push' => [
"season_id" => '$season_id'
]
]
]
]
]
]);
});
output:
need to group all data based on competition_id and need all the data.
Remove $ prefix from groupBy, boundaries, default, and output.
The $bucket 'groupBy' field must be defined as a $-prefixed path or an expression
It says group by field required reference field that should be prefix by $, but you have assigned it in array bracket [].
Try below syntax, i have not tested yet but it would be,
$data = DB::connection('mongodb_football')->collection('match_list')->raw(function ($collection) {
return $collection->aggregate([
[
'$bucket' => [
'groupBy' => '$competition_id',
'boundaries' => ["4zp5rzgh3nq82w1","4zp5rzghjjgq82w", "4zp5rzghkzq82w1","4zp5rzghpvnq82w","4zp5rzghx38q82w","4zp5rzghx5q82w1"],
'default' => "Other",
'output' => [
"data" => [
'$push' => [
"season_id" => '$season_id'
]
]
]
]
]
]);
});
Playground
Solution
$data = DB::connection('mongodb_football')->collection('match_list')->raw(function ($collection) {
return $collection->aggregate([
[
'$bucket' => [
'groupBy' => '$competition_id',
'boundaries' => ["4zp5rzgh3nq82w1","4zp5rzghjjgq82w", "4zp5rzghkzq82w1","4zp5rzghpvnq82w","4zp5rzghx38q82w","4zp5rzghx5q82w1"],
'default' => "Other",
'output' => [
"data" => [
'$push' => [
"season_id" => '$season_id'
]
]
]
]
]
]);
});

return user model with eager loading laravel

return $data =InteractiveSession::with('messages.user')->where('exercise_id',$exercise->id)->first();
This code will return data like below
{
"id": 1,
"exercise_id": 48,
"messages": [
{
"id": 1,
"from_user": 69,
"message": "Hi",
"interactive_session_id": 1,
"user": {
"id": 69,
"first_name": "Jobin"
}
}
]}
how can i return data like below??
{
"id": 1,
"exercise_id": 48,
"messages": [
{
"id": 1,
"from_user":{
"id": 69,
"first_name": "Jobin"
},
"message": "Hi",
"interactive_session_id": 1,
}
]}
i have tables interactivesession,message,users, using hasmany relation
You can create a relation for from_user to the user model, and then do return $data = InteractiveSession::with(['messages.user', 'fromUser'])->where('exercise_id',$exercise->id)->first(); (remember fromUser should be your relation name)
Try this:
$data = InteractiveSession::with('messages.user')->where('exercise_id',$exercise->id)->first();
$data->transform(function($record) {
$messages = $record->messages->transform(function($message) {
'id' => $message->id,
'from_user' => [
'id' => $message->user->id,
'first_name' => $message->user->first_name
],
'message' => $message->message,
'interactive_session_id' => $message->interactive_session_id
});
return [
'id' => $record->id,
'exercise_id' => $record->exercise_id,
'messages' => $messages
];
});

How can i use php conditions inside json data?

Below is the code inside my CategoryResource.php
[
'id' => $this->id,
'name' => $this->name,
'categories' => CategoryResource::collection($this->children),
'items' => ItemListResource::collection($this->items)
];
and by this i am getting response like:
{
"status": true,
"data": {
"categories": [
{
"id": 10,
"name": "Fast Food",
"categories": [
{
"id": 11,
"name": "Pizza",
"categories": [],
"items": [
{
"id": 9,
"name": "Ham \u0026 Crab Stick",
"price": "20.00",
"image": "up/products/c11/Mrn4OpjngYRCkRJ28rJ1LbXUOXy0XjTRyXU7eFoi.jpg"
}
]
}
],
"items": []
}
]
}
}
i dont want "categories" : [] when category does not exist for a category.
please suggest some ideas or ways i can deal with this issue.
I assume that CategoryResource::collection($this->children) returns an array, maybe empty or not.
If that's true, then:
$dataArray = [
'id' => $this->id,
'name' => $this->name,
'items' => ItemListResource::collection($this->items)
];
$categories = CategoryResource::collection($this->children);
if (count(categories) > 0) $dataArray['categories'] = $categories;

Resources