I tried to find a solution here but nothing worked. I want to return values from TagResource using MealResource because I have TagTranslations table and I'm getting the data from the table with translations in TagResource.
Relationships are correctly formed, meal and tag models are connected via meal_tags table and tagtranslations belongsTo Tag::class.
I used TagResource like this:
class TagResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
$translation = $this->translations->where('tag_id', $this->id)->first();
return
[
'id' => $this->id,
'title' => $translation->title,
'slug' => $translation->slug,
];
}
}
and MealResource like this:
public function toArray($request)
{
$translation = $this->translations->where('meal_id', $this->id)->first();
$category_translation = $this->category->translations->where('category_id', $this->category->id)->first();
return [
'id' => $this->id,
'title' => $translation->title,
'decription' => $translation->description,
'category' => [
'id' => $this->category->id,
'title' => $category_translation->title,
'slug' => $category_translation->slug,
],
'tags' => FILL IN THE BLANK (have used TagResource:collection() and new TagResource()) and didnt work
];
}
public function toArray($request)
{
$translation = $this->translations->where('meal_id', $this->id)->first();
$category_translation = $this->category->translations->where('category_id', $this->category->id)->first();
return [
'id' => $this->id,
'title' => $translation->title,
'decription' => $translation->description,
'category' => [
'id' => $this->category->id,
'title' => $category_translation->title,
'slug' => $category_translation->slug,
],
'tags' => TagResource::collection($this->tags),
];
}
If all the Relationships namings/mappings are correct then this will work.And please make sure that model are perfectly mapped respectively.
Related
I'm just learning laravel. I want update key / value in database with laravel api but not work.
My products model is one to many with ProductMeta and many to many with contents model.
My Models
class Product extends Model
{
use HasFactory;
protected $guarded = [];
public function productMeta()
{
return $this->hasMany(ProductMeta::class);
}
public function content()
{
return $this->belongsToMany(Content::class, 'product_contents')->withTimestamps();
}
}
class ProductMeta extends Model
{
use HasFactory;
protected $guarded = [];
public function products()
{
return $this->belongsTo(Product::class);
}
}
class Content extends Model
{
use HasFactory;
protected $guarded= [];
public function product()
{
return $this->belongsToMany(Product::class, 'product_contents');
}
Controller
public function update(Request $request, $id)
{
$product = Product::findOrFail($id);
DB::table('product_metas')
->upsert(
[
[
'product_id' => $product->id,
'key' => 'name',
'value' => $request->name,
],
[
'product_id' => $product->id,
'key' => 'price',
'value' => $request->name,
],
[
'product_id' => $product->id,
'key' => 'amount',
'value' => $request->name,
],
],
['product_id','key'],
['value']
);
return \response()->json([], 204);
}
Table Structure
API parameter
I tried with update and updateOrcreate and updateOrInsert and upsert methods.
just in upsert method writed database but inserted new data.not updated.
In your case, you should use updateOrCreate() instead of upsert.
Product::updateOrCreate([
'product_id' => $id,
'name' => $request->name,
'price' => $request->price,
'amount' => $request->amount
]);
or
Product::upsert([
[
'product_id' => $id,
'name' => $request->name,
'price' => $request->price,
'amount' => $request->amount
]
], ['product_id'], ['name', 'price', 'amount']);
In addition your problem is your table name is not matching with your structure table name. In your controller DB::table('product_metas') should be DB::table('products_meta').
my problem solved this way:
ProductMeta::query()->where('product_id', $id)->upsert([
['product_id' => $id, 'key' => 'name', 'value' => $request->name],
['product_id' => $id, 'key' => 'price', 'value' => $request->price],
['product_id' => $id, 'key' => 'amount', 'value' => $request->amount]],
['product_id'], ['value']);
$contentRecord = Product::find($id);
$contentRecord->content()->update(['path'=>$request->path]);
return response()->json([], 204);
I forget use query() method for ProductMeta and added $table->unique(['product_id', 'key']); to product meta migration.
**products relation one to many with product Meta
And Many to many with content.
I'm developing an API with Laravel. In one of the endpoint I'm accessing, some fields are showing a null value, but it should have some information.
Note the "addicionais_descricao" and "valor" fields, both always come with null values when I include them in the attributeitems array, but if I leave it at the initial level, the data is presented, but it doesn't solve my case, because I need this information with the attribute items:
enter image description here
This is where the endpoint calls, I make the query in the "Attribute" table, which has a relationship with the "Attributeitems" table, while the "attributeitems" table is linked to "Attribute" and "product".
public function show($id)
{
$atributos = Atributo::query('atributo')
->select(
'atributo.id',
'atributo.atrdescricao',
'atributoitens.atributo_id',
'atributoitens.produto_id',
'produto.prodescricao',
'produto.provalor'
)
->leftJoin('atributoitens', 'atributo.id', '=', 'atributoitens.atributo_id')
->leftJoin('produto', 'produto.id', '=', 'atributoitens.produto_id')
->where('atributo.id', '=', $id)
->get()->unique('id');
return AtributoResource::collection($atributos);
}
Resource Atributo:
public function toArray($request)
{
return [
'id' => $this->id,
'descricao' => $this->atrdescricao,
'atributoitens' => AtributoitensResource::collection($this->atributoitens),
];
}
Resource Atributo Itens:
public function toArray($request)
{
return [
'id' => $this->id,
'atributo' => $this->atributo_id,
'produtos' => $this->produto_id,
'adicionais_descricao' => $this->prodescricao,
'valor' => $this->provalor
];
}
What is the correct procedure for this situation?
Take this example as a reference :
Controller
$data = $shop->products()
->whereStatus(true)
->where('product_shop.active', true)
->where('product_shop.quantity', '>=', $this->min_product_qty)
->paginate(50);
return (new ProductCollection($data))
->response()
->setStatusCode(200);
ProductCollection
public function toArray($request)
{
return [
'data' => $this->collection
->map(function($product) use ($request) {
return (new ProductResource($product))->toArray($request);
}),
'brand' => $this->when($request->brand, $request->brand)
];
}
ProductResource
public function toArray($request)
{
return [
'type' => 'product',
'id' => (string) $this->id,
'attributes' => [
'uuid' => $this->uuid,
'name' => $this->name,
'slug' => $this->slug,
'description' => $this->description,
'thumb_path' => $this->thumb_path,
'cover_path' => $this->cover_path,
],
'relationships' => [
'brand' => $this->brand
]
];
}
Something like this should help you do what you want. I cant exactly do it for you. by the way why you are not using Eloquent, something like
Attribute::where(...)->with(['relation_1', 'products'])->get();
public function toArray($request)
{
return [
'id' => $this->id,
'attributes' => [...],
'products' => $this->collection
->map(function($this->product) use ($request) {
return (new ProductResource($product))->toArray($request);
}),
];
}
I have multiple resources and mostly resource content few fields that are same for all other resource and it's very difficult to modify all the resource in case I need to update/add key/value in the resource.
Is there any way that I can create one main resource that will contain all common fields and then call the main resource in my another resource and add few additional fields.
Here is my controller where I am calling CitizenFeedResource file.
if ($events->total()) {
return CitizenFeedResource::collection($events);
}
This is my CitizenFeedResource file.
use Illuminate\Http\Resources\Json\JsonResource;
class CitizenFeedResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'description' => $this->description,
'start_timestamp' => optional($this->start_timestamp)->toDateTimeString(),
'end_timestamp' => optional($this->end_timestamp)->toDateTimeString(),
'location' => [
'name' => $this->location,
'landmark' => $this->landmark,
'coordinates' => $this->coordinates,
'city' => $this->city,
],
'open_event' => $this->open_event,
'full_day_event' => $this->full_day_event,
'banner' => $this->banner,
'url' => $this->path(),
'web_url' => $this->webUrl(),
'categories' => $this->categories,
'timestamp' => $this->created_at->toDateTimeString(),
'timestamp_ago' => $this->created_at->diffForHumans(),
'statistics' => $this->statistics,
'additional_details' => $this->additionalDetails(),
'municipal_details' => $this->municipal_details,
'user' => optional($this->user)->getProfile($this->channel, '1.1'),
'complaint_id' => $this->complaint_id,
'volunteers' => (isset($this->volunteers) && $this->volunteers) ? $this->user->getVolunteerProfile($this->volunteers, '1.1') : array(),
'share_count' => (isset($this->statistics) && isset($this->statistics['share_count'])) ? array_sum($this->statistics['share_count']) : 0,
'volunteer_status' => $this->getVolunteerStatus($request),
'editable' => $this->editable(),
'type' => 'Event',
];
}
}
You don't have to extend directly from JsonResponse, so you can create one main object let's say like this:
use Illuminate\Http\Resources\Json\JsonResource;
class BaseResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'description' => $this->description,
];
}
}
and then
class CitizenFeedResource extends BaseResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
$data = parent::toArray($request);
$data['custom_field'] = $this->custom_field;
// ...
return $data;
}
}
I'm passing data to these functions via Axios/Vue. The Eloquent interactions work perfectly. When I store (i.e. create a new call) the resource returns as expected. When I update a record, it updates in the database, however, I get a blank response. In other words the return new CallResource($call) returns nothing. I can't work out where I've gone wrong.
public function store(Request $request)
{
$call = $this->validate($request, [
'title' => 'required',
'job_id' => 'required',
'location' => 'required',
'starts' => 'required|date|before:ends',
'ends' => 'required|date|after:starts',
'rate' => 'required'
]);
$call = Call::create($call);
return new CallResource($call);
}
public function update(Request $request, $id)
{
$data = $this->validate($request, [
'title' => 'required',
'job_id' => 'required',
'location' => 'required',
'starts' => 'required|date|before:ends',
'ends' => 'required|date|after:starts',
'rate' => 'required'
]);
$call = Call::find($id);
$call->update($data);
return new CallResource($call);
}
The call resource is really simple
class CallResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'location' => $this->location,
'starts' => $this->starts,
'ends' => $this->ends,
'rate' => $this->rate
];
}
}
Laravel 5.6 fyi.
i have Post and User model with one to one relation and it works good:
//User.php
public function post(){
return $this->hasOne(Post::class);
}
// Post.php
public function user() {
return $this->belongsTo(User::class);
}
now i create API resources:
php artisan make:resource Post
php artisan make:resource User
I need to return all post with an api call then i set my route:
//web.php: /resource/posts
Route::get('/resource/posts', function () {
return PostResource::collection(Post::all());
});
this is my Posts resource class:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\Resource;
use App\Http\Resources\User as UserResource;
class Posts extends Resource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'slug' => $this->slug,
'bodys' => $this->body,
'users' => UserResource::collection($this->user),
'published' => $this->published,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}
this is the error:
Call to undefined method Illuminate\Database\Query\Builder::mapInto()
if i remove:
'users' => UserResource::collection($this->user),
it's work but i need to include relations in my api json, i have read and followed the doc at https://laravel.com/docs/5.5/collections.
this is my User resource class:
```
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\Resource;
class User extends Resource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'user_id' => $this->user_id,
'name' => $this->name,
'lastname' => $this->lastname,
'email' => $this->email
];
}
}
any ideas where am i wrong?
The problem is that you use UserResource::collection($this->user) and you have just one element not a collection so you can replace it with new UserResource($this->user) like this :
return [
'id' => $this->id,
'title' => $this->title,
'slug' => $this->slug,
'bodys' => $this->body,
'users' => new UserResource($this->user),
'published' => $this->published,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
In Laravel 8.5.*, you can use the static method make on the collection to get the same result. It would be like UserResource::make($this->user)
this problem is that you use UserResource::collection($this->user) it mean you have many users but and you have just one element for the a single resource and not a collection so you can replace it with new UserResource($this->user)
when you have just one element and not a collection use new Resouce
return [
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'user' => new UserResource($this->whenLoaded('user')),
];
and give a try to use the eager loading for better perfomance not just this.
using the method whenLoaded and put the relations name in ( ).
Resource::collection is used when the model has hasMany relation. If your model has hasOne relation use Resource::make(Book::all())