How to get only specific fields from Laravel Eloquent API Resource? - php

I am working on an API that will be accessible via a mobile and web app.
I need to send specific fields from the API resource.
I have ItemResource
public function toArray($request) {
return [
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'price' => $this->price,
'stock' => $this->stock,
'reviews' => $this->reviews, // this is from reviews table
];
}
My Item Model
public function reviews() {
return $this->hasMany(ItemReview::class);
}
My controller code
return ItemResource::collection(Item::all());
I get this array
{
"data": {
"id": 10,
"name": "Item Name",
"description": "Item description",
"price": 410,
"stock": "815",
"reviews": [
{
"id": 4, // I don't want to get this field
"item_id": 10, // I don't want to get this field
"user_id": 9, // I want to show user name instead of user id
"review": "item review."
},
{
"id": 12, // I don't want to get this field
"item_id": 10, // I don't want to get this field
"user_id": 3, // I want to show user name instead of user id
"review": "item review."
}
]
}
}
I want to get an array like this
{
"data": {
"id": 10,
"name": "Item Name",
"description": "Item description",
"price": 410,
"stock": "815",
"reviews": [
{
"user_name": 'jhon',
"review": "item review."
},
{
"user_name": 'jhon2',
"review": "item review."
}
]
}
}
I would like to show only specific fields.
How can I implement this?

1. Only select what you need
If you add parentheses after reviews, you can return a Builder instance, which will allow you to add queries to the relationship.
With this, you can select the fields you need.
public function toArray($request) {
return [
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'price' => $this->price,
'stock' => $this->stock,
'reviews' => $this->reviews()->select('user_name', 'review')->get(),
];
}
2. Create a ReviewResource
You can also create a resource for Review that returns it's own data.
class ReviewResource extends JsonResource
{
public function toArray($request)
{
return [
'user_name' => $this->user_name,
'review' => $this->review,
];
}
}
class ItemResource extends JsonResource
{
public function toArray($request) {
return [
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'price' => $this->price,
'stock' => $this->stock,
'reviews' => ReviewResource::collection($this->reviews),
];
}
}

Related

When creating a new record in Laravel, I am unable to obtain relation data

I'm basically trying to save comments in my database, so when a new comment is created, I want a newly created comment object with user details, but I can't figure out how to do this.
here is store method in my controller
public function store(Request $request)
{
$request->validate([
'comment' => 'required|string',
]);
$comment = Comment::create([
'ticket_id' => $request->ticket_id,
'user_id' => auth()->user()->id,
'comment' => $request->comment,
]);
return response()->json($comment);
}
Response
{
"ticket_id": 19,
"user_id": 1,
"comment": "dummy comment",
"updated_at": "2022-07-16T12:32:31.000000Z",
"created_at": "2022-07-16T12:32:31.000000Z",
"id": 33
}
In Comment Model
public function user()
{
return $this->hasOne(User::class, 'id', 'user_id');
}
Expecting Result
{
"id": 26,
"comment": "new commnet",
"ticket_id": 19,
"user_id": 1,
"created_at": "2022-07-16T12:16:19.000000Z",
"updated_at": "2022-07-16T12:16:19.000000Z",
"user": {
"id": 1,
"name": "admin",
"email": "admin#localhost",
"email_verified_at": null,
"created_at": "2022-07-16T08:35:04.000000Z",
"updated_at": "2022-07-16T08:35:04.000000Z",
}
}
in Comment Model
public function user(){
return $this->belongsTo(User::class,'user_id');
}
when get comments with user
$comments = Comment::with('user')->get();
Edit
if you want use user object directly when create
if user is me
$comment = Comment::create([
'ticket_id' => $request->ticket_id,
'user_id' => auth()->user()->id,
'comment' => $request->comment,
]);
$comment['user'] = auth()->user();
return response()->json($comment);
Last Edit it's work
return response()->json($comment->load('user'));

add values ​from a collection to others using a column as a reference (PHP, LARAVEL)

I am working with php in laravel, in this case I have 2 collections of objects, one of them looks like this:
[
{
"id": 1,
"name": "product x",
"quantity": "100",
},
{
"id": 2,
"codProd": "product y",
"quantity": "200",
},
{
"id": 3,
"name": "product a.",
"quantity": "30",
}
]
and the other looks like this:
[
{
"reference": 1,
"quantity": "80",
},
{
"reference": 2,
"quantity": "50",
},
]
What I need is to keep the first collection but adding the value of the quantity key from the second collection, using the reference key as a relationship with the id of the first collection, the final result should look like this:
[
{
"id": 1,
"name": "product x",
"quantity": "180",
},
{
"id": 2,
"codProd": "product y",
"quantity": "250",
},
{
"id": 3,
"name": "product a.",
"quantity": "30",
}
]
so how can I do this?, any guide or help I am grateful
You can use a collection method for this, the map method:
$collection_one = collect([
[
'id' => 1,
'name' => 'product x',
'quantity' => 100,
],
[
'id' => 2,
'name' => 'product y',
'quantity' => 100,
],
[
'id' => 1,
'name' => 'product a.',
'quantity' => 30,
],
]);
$collection_two = collect([
[
'reference' => 1,
'quantity' => 80,
],
[
'reference' => 2,
'quantity' => 50,
],
]);
For you to get the collection what you want:
$collect = [];
$collect = $collection_one->map(function ($value_one) use ($collection_two) {
if ($search = $collection_two->firstWhere('reference', '=', $value_one['id'])) {
$value_one['quantity'] += $search['quantity'];
}
return $value_one;
});
Ready, that worked for me. :)

Laravel resource collection - paginate - JSON response error

I want to do paging at the Laravel API. As a result, I want to get data and status code type.
Controller :
public function index()
{
$data = PersonCollection::collection(Person::paginate(2));
return response()->json($data, 200);
}
PersonCollection Resource :
public function toArray($request)
{
return [
'id' => $this->id,
'first_name' => $this->first_name,
'last_name' => $this->last_name,
'email' => $this->email,
'phone' => $this->phone,
'city' => $this->city,
'href' => [
'link' => route('person.show', $this->id),
],
];
}
Output :
https://i.hizliresim.com/LvlBzj.png
[
{
"id": 1,
"first_name": "Burak Ali",
"last_name": "Ildır",
"email": "burak#gmail.com",
"phone": "376.395.7233",
"city": "Koelpinstad",
"href": {
"link": "http://127.0.0.1:8000/api/v2/person/1"
}
},
{
"id": 2,
"first_name": "Vena",
"last_name": "Spinka",
"email": "shields.carolyn#example.org",
"phone": "716-268-7788 x092",
"city": "South Gudrunbury",
"href": {
"link": "http://127.0.0.1:8000/api/v2/person/2"
}
}
]
But I want.
https://i.hizliresim.com/LvlBWo.png
{
"data": [
{
"id": 1,
"first_name": "Burak Ali",
"last_name": "Ildır",
"email": "burak#gmail.com",
"phone": "376.395.7233",
"city": "Koelpinstad",
"href": {
"link": "http://127.0.0.1:8000/api/v2/person/1"
}
},
{
"id": 2,
"first_name": "Vena",
"last_name": "Spinka",
"email": "shields.carolyn#example.org",
"phone": "716-268-7788 x092",
"city": "South Gudrunbury",
"href": {
"link": "http://127.0.0.1:8000/api/v2/person/2"
}
}
],
"links": {
"first": "http://127.0.0.1:8000/api/v1/person?page=1",
"last": "http://127.0.0.1:8000/api/v1/person?page=26",
"prev": null,
"next": "http://127.0.0.1:8000/api/v1/person?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 26,
"path": "http://127.0.0.1:8000/api/v1/person",
"per_page": 2,
"to": 2,
"total": 52
}
}
I want other page links. But when I convert it to JSON data, links and metadata do not come.
A resource collection describes how a collection of models will be transmitted as JSON.
You seem to be using it for an individual model. What you need is:
public function toArray($request)
{
return $this->collection->map(function ($person) {
return [
'id' => $person->id,
'first_name' => $person->first_name,
'last_name' => $person->last_name,
'email' => $person->email,
'phone' => $person->phone,
'city' => $person->city,
'href' => [
'link' => route('person.show', $person->id),
],
];
});
}
However the recommended way is to make a PersonResource instead in the same namespace and implement the toArray($request) in that class:
Person
class Person extends Resource //The name is important
{
public function toArray($request)
{
return [
'id' => $this->id,
'first_name' => $this->first_name,
'last_name' => $this->last_name,
'email' => $this->email,
'phone' => $this->phone,
'city' => $this->city,
'href' => [
'link' => route('person.show', $this->id),
],
];
}
}
PersonCollection
class PersonCollection extends ResourceCollection
{
// This class is intentionally empty
}
Finally you should let Laravel handle how to make the response:
public function index()
{
$data = PersonCollection::collection(Person::paginate(2));
return $data->toResponse();
}
The default behaviour of the resource collection is to look for a resource which is named like the collection but with the Collection part removed (in this case PersonCollection will look for a Person resource).
This should ensure each model is converted according to the resource and the pagination behaviour is maintained.

Laravel Resource - Parents with Childrens Categories

I want to load my childrens categories in the parent with Laravel Resource.
My json is like that :
"data": [
{
"id": 2,
"slug": "child-categorie-0",
"name": "Child Catégorie 0",
"parent_id": 1
},
{
"id": 3,
"slug": "child-categorie-1",
"name": "Child Catégorie 1",
"parent_id": 1
},
{
"id": 1,
"slug": "parent-categorie-0",
"name": "Parent Catégorie 0",
"parent_id": null
},
],
I would like to be like that :
"data": [
{
"id": 1,
"slug": "parent-categorie-0",
"name": "Parent Catégorie 0",
"childrens": [
{
"id": 2,
"slug": "child-categorie-0",
"name": "Child Catégorie 0",
},
{
"id": 3,
"slug": "child-categorie-1",
"name": "Child Catégorie 1",
}
]
}
],
App\Http\Resources\Category.php
public function toArray($request)
{
return [
'id' => $this->id,
'slug' => $this->slug,
'name' => $this->name,
'parent_id' => $this->parent_id
];
}
Controller
$categories = Category::orderBy('name', 'asc')->get();
return new CategoryCollection($categories);
I'm new with Laravel Resource so if someone have a path for solving this problem, thanks !
I solve my problem !
I create a relationship in my Category Model.
public function childrens()
{
return $this->hasMany('App\Category', 'parent_id', 'id');
}
In my Category Resource
public function toArray($request)
{
return [
'slug' => $this->slug,
'name' => $this->name,
'childrens' => self::collection($this->whenLoaded('childrens'))
];
}
In my Controller
$categories = Category::with('childrens')->parents()->orderBy('name','asc')->get();

Laravel 5.5 API Resource Collection using array

I am using laravel 5.5 API resource collection for showing list of products. I am using an array instead of the modal object. I got my list of items but i am not sure how to show item variant array in the response. The actual response array passing to the api resource are:
{
"ID": 3160,
"UserID": 3302,
"ItemName": "2",
"Description": "2",
"Price": 2,
"StockLimited": true,
"StockQuantity": 19,
"CurrencyCode": "USD",
"ImageUrl": "/images/items/Item3302-636434761494584365-qq5HFm.png",
"VariantItems": [
{
"ID": 3181,
"ItemName": "2",
"Price": 0,
"StockLimited": false,
"StockQuantity": 0,
"CurrencyCode": "USD",
"Variants": [
{
"Id": 1099,
"Name": "Red",
"VariantGroupId": 1027,
"VariantGroupName": "Colour"
},
{
"Id": 1111,
"Name": "S",
"VariantGroupId": 1028,
"VariantGroupName": "Size"
}
]
},
{
"ID": 3182,
"ItemName": "2",
"Price": 0,
"StockLimited": false,
"StockQuantity": 0,
"CurrencyCode": "USD",
"Variants": [
{
"Id": 1099,
"Name": "Red",
"VariantGroupId": 1027,
"VariantGroupName": "Colour"
},
{
"Id": 1112,
"Name": "M",
"VariantGroupId": 1028,
"VariantGroupName": "Size"
}
]
}
],
"MerchantName": "seller",
"VariantGroupLists": [
{
"VariantGroupName": "Colour",
"Names": [
"Red",
"Grey",
"Navy"
]
},
{
"VariantGroupName": "Size",
"Names": [
"S",
"M",
"L",
"XL"
]
}
]
}
My ItemResourceCollection
public function toArray($request)
{
return [
'data' => $this->collection->map(function ($item) use ($request) {
return (new ItemResource($item))->toArray($request);
})
];
}
My ItemResource
public function toArray($request)
{
return [
"item_id" => $this->resource->ID,
"item_name" => $this->resource->ItemName,
"description" =>$this->resource->Description,
"item_price"=>$this->resource->Price,
"stock_qty"=>$this->resource->StockQuantity,
"currency_code"=>$this->resource->CurrencyCode,
"item_image_url"=>$this->resource->ImageUrl,
];
}
public function with($request)
{
return ['item_varients' => [new ItemResource($this->resource->VariantItems)]];
}
What I am trying to achieve is this result:
[
{
"item_id": 3160,
"item_name": "BLACK COWL NECK DRESS WITH DIAMANTE DETAIL",
"description": "Test test",
"item_price": 16.99,
"stock_qty": 20,
"currency_code": "SGD",
"item_image_url": "/images/items/Item18-636231488325192562-GoiIBl.png",
"item_varients": [
{
"id": 29,
"name": "Red",
"varientgroupId": 11,
"varientgroupname": "Color"
}
]
}
]
the with() method in ItemResource is not working. How do I add the "item_varients" array in the resource response?
I would first move the line to add the item_varients property inside the method toArray like following:
public function toArray($request)
{
return [
"item_id" => $this->resource->ID,
"item_name" => $this->resource->ItemName,
"description" => $this->resource->Description,
"item_price" => $this->resource->Price,
"stock_qty" => $this->resource->StockQuantity,
"currency_code" => $this->resource->CurrencyCode,
"item_image_url" => $this->resource->ImageUrl,
"item_varients" => [
new ItemResource($this->resource->VariantItems)
]
]
}
Meta information
We do that because the with is meant for to add a meta information block or something similar. See https://laravel.com/docs/5.5/eloquent-resources#adding-meta-data
The advantage when using the with method is, that multiple resources results in only one meta block. This is not what you want, I think.
Resource for VariantItem
I think you have to build a Resource class also for VariantItem. And because the VariantItems are a collection you also have to fill your array called item_variants similar to your ItemResourceCollection. So change your code to look like this:
public function toArray($request)
{
/**
* Prepare your variant items to have a collection
*
* #var Collection $variantItems
*/
$variantItems = collect($this->resource->VariantItems);
return [
"item_id" => $this->resource->ID,
"item_name" => $this->resource->ItemName,
"description" => $this->resource->Description,
"item_price" => $this->resource->Price,
"stock_qty" > $this->resource->StockQuantity,
"currency_code" => $this->resource->CurrencyCode,
"item_image_url" => $this->resource->ImageUrl,
"item_varients" => VariantItem::collection($variantItems)
]
}
Here is the best way to get specific fields using a collection
return [
'id' => $this->id,
'variant_item' => new CustomResource($this->user, ['item_id', 'item_name', 'item_size'])
];

Categories