How can i use php conditions inside json data? - laravel

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;

Related

How to pull actual values from foreign id when an array of ids is returned?

I am making an e-commerce site where the person can make an offer for the product. And I want to display all the offers on product at a place where I get the product.After that want to get the information about the user who made the offer and the parent offer in the same query.
I am getting the product like this
`public function show(Product $product)
{
// return response()->json($product);
return new SingleProductResource($product);
}`
The SingleProductResource returns the following
`public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'description' => $this->description,
'type' => $this->type,
'images' => $this->images,
'status' => $this->status,
'owner' => $this->user,
'offers' => $this->offers,
];
}`
Offers returns an array like this
`"offers": [
{
"id": 2,
"user_id": null,
"product_id": 1,
"offer_amount": "3",
"parent_id": 2,
"created_at": "2022-11-12T07:54:10.000000Z",
"updated_at": "2022-11-12T07:54:10.000000Z"
},
{
"id": 4,
"user_id": 1,
"product_id": 1,
"offer_amount": "3",
"parent_id": 2,
"created_at": "2022-11-12T08:01:29.000000Z",
"updated_at": "2022-11-12T08:01:29.000000Z"
},
{
"id": 5,
"user_id": null,
"product_id": 1,
"offer_amount": "3",
"parent_id": null,
"created_at": "2022-11-12T08:01:56.000000Z",
"updated_at": "2022-11-12T08:01:56.000000Z"
}
]`
But I want to get the user information directly in this query.
I cannot do this(see comment below) inside the resource as $this->offers returns an array.
`return [
'id' => $this->id,
'title' => $this->title,
'description' => $this->description,
'type' => $this->type,
'images' => $this->images,
'status' => $this->status,
'creator' => $this->user,
'offers' => $this->offers->user, //this
];`
You will need to trim your $product in some way. For example
$products = Product::leftJoin('offers', 'products.id', 'offers.product_id')
->where('user_id')
->where('products.id', $product['id'])
->get();
return new SingleProductResource::collection($products);

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();

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
];
});

Laravel reject function on nested collection

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"
}
]
}
]
}

Elasticsearch: Is it possible to sort collapsed results by a nested field?

I have a fairly complex mapping which is storing products, and within each document it contains a nested array of pre-calculated prices for each customer.
There may be multiple versions of each product in the index (with unique codes). Alternative products are grouped by a common xrefs_hash. The query I'm writing needs to select the best product for each customer (i.e. aggregate/collapse on the xrefs_hash), and then select the top product based on the value of the prices.weight nested field.
The prices.weight field is a float which we've pre-calculated based on the shops' customer settings on how they want to prioritise their own items. A hash is created from these settings (stored in prices.pricing_hash) so that we can store a single set of pricing if multiple customers share the same settings.
The index contains up to 300,000 products and can end up with ~100,000,000 documents once all prices are calculated and inserted.
The mapping looks something like this (shortened for brevity):
'mappings' => [
'_source' => [
'enabled' => true,
],
'dynamic' => false,
'properties' => [
'dealer_item_id' => [
'type' => 'integer',
],
'code' => [
'type' => 'text',
'analyzer' => 'custom_code_analyzer',
'fields' => [
'raw' => [
'type' => 'keyword',
],
],
],
'xrefs' => [
'type' => 'text',
'analyzer' => 'custom_code_analyzer',
'fields' => [
'raw' => [
'type' => 'keyword',
],
],
],
'xrefs_hash' => [
'type' => 'keyword',
],
'title' => [
'type' => 'text',
'analyzer' => 'custom_english_analyzer',
'fields' => [
'ngram_title' => [
'type' => 'text',
'analyzer' => 'custom_title_analyzer',
],
'raw' => [
'type' => 'keyword',
],
],
],
...
'prices' => [
'type' => 'nested',
'dynamic' => false,
'properties' => [
'pricing_hash' => [
'type' => 'keyword',
'index' => true,
],
'unit_price' => [
'type' => 'float',
'index' => true,
],
'pricebreaks' => [
'type' => 'object',
'dynamic' => false,
'properties' => [
'quantity' => [
'type' => 'integer',
'index' => false,
],
'price' => [
'type' => 'integer',
'index' => false,
],
],
],
'weight' => [
'type' => 'float',
'index' => true,
],
],
],
],
],
Example documents:
{
"dealer_item_id": 122023,
"code": "ABC123A",
"xrefs": [
"ABC123A",
"ABC123B",
],
"title": "Product A",
"xrefs_hash": "16d5415674c8365f63329b11ffc88da109590cec",
"prices": [
{
"pricebreaks": [
{
"quantity": 1,
"price": 9.75,
"contract": false
}
],
"weight": 0.20512820512820512,
"pricing_hash": "aabe06b7",
"unit_price": 9.75,
},
{
"pricebreaks": [
{
"quantity": 1,
"price": 9.75,
"contract": false
}
],
"weight": 0.20512820512820512,
"pricing_hash": "73643f3b",
"unit_price": 9.75,
}
]
},
{
"dealer_item_id": 124293,
"code": "ABC1234B",
"xrefs": [
"ABC123A",
"ABC123B",
],
"title": "Product B",
"xrefs_hash": "16d5415674c8365f63329b11ffc88da109590cec",
"prices": [
{
"contract_item": false,
"pricebreaks": [
{
"quantity": 1,
"price": 7.39,
"contract": false
}
],
"weight": 0.33829499323410017,
"pricing_hash": "aabe06b7",
"unit_price": 7.39,
},
{
"pricebreaks": [
{
"quantity": 1,
"price": 9.75,
"contract": false
}
],
"weight": 0.20512820512820512,
"pricing_hash": "73643f3b",
"unit_price": 9.75,
}
]
},
Example query:
{
"track_total_hits": 100000,
"query": {
"bool": {
"filter": {
"bool": {
"must": [
{
"nested": {
"path": "prices",
"score_mode": "none",
"inner_hits": {
"_source": {
"include": [
"prices"
]
}
},
"query": {
"bool": {
"must": [
{
"term": {
"prices.pricing_hash": "aabe06b7"
}
}
]
}
}
}
},
{
"term": {
"code.raw": "RX58022"
}
}
],
"must_not": [
{
"term": {
"disabled": true
}
}
]
}
}
}
},
"_source": {
"includes": [
"code",
"dealer_item_id",
"title",
"xrefs"
]
},
"collapse": {
"field": "xrefs_hash",
"inner_hits": {
"name": "best_xrefs",
"sort": {
"prices.weight": "desc"
},
"size": 1
}
},
"aggregations": {
"xrefs_count": {
"cardinality": {
"field": "xrefs_hash",
"precision_threshold": 40000
}
}
}
}
I have tried using a collapse query to select the best product, but this does not seem to support sorting by the nested prices.weight field.
I've also tried aggretating based on the xrefs_hash, but this seems to make pagination at the category level impossible.
The above example query almost works, but does not return the collapsed results in the correct order. When inspecting the query it seems to be replacing the collapse sort with Infinity, which apparently ES does if a document does not contain a sort field.
So what I'm wondering is; is it possible to:
Return 1 document per unique xref_hash value
Return the specific document whith the highest prices.weight value, matching customer's pricing_hash
Also make this work with pagination

Resources