I'm trying to create an API resource, where I can view a response build from three tables.
Store
Orders
Purchases
Currently, I can go to a store (there are multiple stores) using routes (api/store/orders) and view the orders in a JSON response.
response
{
data: [
{
id: 1,
code: "1f",
status: "waiting",
user_name: "Salman",
created_at: "",
updated_at: ""
},
{
id: 2,
code: "2f",
status: "waiting",
user_name: "Jerome",
created_at: "",
updated_at: ""
}
]
}
However, when I try to add my purchases to the response with:
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class Orderresource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'code' => $this->code,
'status' => $this->status,
'user_name' => $this->user_name,
// Added purchases
'order' => $this->purchases,
'created_at' => (string) $this->created_at,
'updated_at' => (string) $this->updated_at,
];
}
}
I get met with an error response Undefined property: stdClass::$purchases
Now I've added purchases to my order model with:
public function purchases()
{
return $this->hasMany(Purchase::class);
}
However, I know that the order may lie with my show_order_per_store.
function in my OrderController
public function show_order_per_club($id)
{
$order = DB::table('orders')->where('store_id', '=', $id)->get();
return Orderresource::collection($order);
}
Now, this function gets all orders with similar stores, but how do I add the purchases to the stores API response?
In short, I'm trying to get an API response per store with the orders it has, and the purchases belong to that order.
Instead of using DB:table for querying, try using the model directly:
Order::where('store_id', '=', $id)->get();
Seems DB:table returns stdClass objects, so it lacks all the virtual attributes that a model might provide.
Related
I am trying to return pivot data to a resource.
The pivot table works, I can add and remove entrys like expected, but I am not able to get the user_id returned in ActivityResource...
In the Laravel Documentation it looks so easy, am I missing something?
// Activity.php
class Activity extends Model
{
public function members()
{
return $this->belongsToMany('App\User', 'activity_user', 'user_id', 'activity_id')->withPivot('activity_id','user_id')->withTimestamps();
}
}
// User.php
class User extends Authenticatable
{
public function joinedactivities()
{
return $this->belongsToMany('App\Activity');
}
}
In my ActivityController I want to return a newly created ActivityResource with 'eager-loaded' relationship
// ActivityController
public function show($id)
{
$activity = Activity::with('members')->findOrFail($id);
/* foreach ($activity->members as $user) {
echo $user->id . " "; // With this I can actually see the right ids
}
return;*/
return new ActivityResource($activity);
}
ActivityResource:
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'attendees' => $this->whenPivotLoaded('activity_user', function () {
return $this->pivot->user_id;
}),
];
}
I dont get any errors instead the attendees field is just not returned. I tried so many things, struggeling with that. Help very appreciated.
I am using Laravel 6.
->withPivot('activity_id','user_id') is not needed. Those fields will appear on your relation object no matter what. For the resource, I think you can do the following:
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
// If the relation 'members' is loaded, get an array of user ids otherwise, return null
'attendees' => $this->relationLoaded('members') ? $this->members->pluck('pivot.user_id')->unique()->all() : null
];
}
The main problem is the relationship is a Many to Many, meaning there's more than 1 pivot. With this solution, your object will look like this.
{
id: 3,
title: 'A Title',
attendees: [
1,
2,
3,
],
}
If you want the ids concatenated in a single string like in your commented foreach, replace all() by join(' ')
// If the relation 'members' is loaded, get an string of user ids otherwise, return null
'attendees' => $this->relationLoaded('members') ? $this->members->pluck('pivot.user_id')->unique()->join(' ') : null
{
id: 3,
title: 'A Title',
attendees: '1 2 3',
}
I have resources data returning in JSON. When I try to get my data with paginate it is not included meta data.
Based on documentation my data supposed to be included meta like:
"meta":{
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "http://example.com/pagination",
"per_page": 15,
"to": 10,
"total": 10
}
but my data is returning like this:
Code
controller
public function index()
{
$products = ProductFrontResource::collection(Product::orderby('id', 'desc')->with(['photos', 'seo', 'tags', 'variations', 'variations.children', 'options', 'options.children', 'categories'])->where('active', 'yes')->paginate(8));
return response()->json([
'data' => $products,
'message' => 'Products retrieved successfully.',
]);
}
Any idea?
You don't need to use response(). Laravel's resource classes allow you to expressively and easily transform your models and model collections into JSON.
Every resource class defines a toArray method which returns the array of attributes that should be converted to JSON when sending the response.
public function index()
{
$data = Product::orderby('id', 'desc')
->with(['photos', 'seo', 'tags', 'variations', 'variations.children', 'options', 'options.children', 'categories'])
->where('active', 'yes')
->paginate(8);
$products = ProductFrontResource::collection($data);
return $products;
}
Additional Meta Data
'message' => 'Products retrieved successfully.'
Yes, you can Adding Meta Data.
public function toArray($request)
{
return [
'data' => $this->collection,
'message' => 'Products retrieved successfully.'
];
}
I was using these codes in my controller to get all the data from my 2 tables and it works fine
$All = Customers::with('order')->paginate(10);
return response()->json([
'code' => 0,
'success' => true,
'data' => $All
], 200);
Here is how I define the relationship between these 2 tables
class Customers extends Model
{
public function order()
{
return $this->hasMany(Orders::class, 'customer_id', 'id');
}
}
class Orders extends Model
{
public function customers()
{
return $this->belongsTo(Customers::class, 'customer_id', 'id');
}
}
Now my desire output is to hide the order id, order timestamps and change the customer_id to customer's name (the customer's name is not in my orders db table).
I'm using 'data' => DataResource::collection($All) in my controller and this is my DataResource
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'order' => $this->order
];
}
and of course the output is same with the image above.
My database structure:
orders table:
customer table:
Can anyone help me with that?
The answer is simple and basically a copy of the official documentation. You simply need to wrap your orders in an OrderResource as well.
// DataResource
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'order' => OrderResource::collection($this->order)
];
}
// OrderResource
public function toArray($request)
{
return [
'items' => $this->items,
'quantity' => $this->quantity
];
}
I don't really understand why you would want to include the customer_name in your orders when it is already present on the customers object one hierarchy above. But if you really want to add it, you should be able to do so with: 'customer_name' => $this->customers->name.
As a side note: you really should be more consistent with your naming. Why is the resource called DataResource when it is about Customers? Why is your model called Customers in plural form rather than Customer in singular, which is the convention (and more logical if you consider that one model represents one customer). Why is your belongsTo relation called customers() in plural when it returns one customer, while your hasMany relation is called order whereas it returns one or more orders?
Laravel Api resource:
Well basically I have a resource which is tied to a model, but this resource which upon fetching, i also obtain the related model resource e.g.
use Illuminate\Http\Resources\Json\Resource;
class ExampleResource extends Resource
{
public function toArray($request)
{
return [
"id" => $this->id,
"user" => new UserResource($this->user),
"total" => number_format($this->getTotal(), 2),
"details" => ExampleDetailsResource::collection($this->details),
];
}
}
So with this, my question is:
when am fetching all Examples, I don't want to fetch their details as well,
But when am fetching an example, I need these details.
So is there a way to inform the resource that, I don't need this details when am fetching the details.
The reason why I need to do this is because, an example can have many details and therefore whenever I fetch all the examples this means I will also get be fetching their details as well which slows down my fetch.
you can create ExampleDetailResource and ExampleResource, when you don't want more detail call ExampleResouce and vice versa. like this
for less detail in response,
use Illuminate\Http\Resources\Json\Resource;
class ExampleResource extends Resource
{
public function toArray($request)
{
return [
"id" => $this->id,
"user" => new UserResource($this->user),
"total" => number_format($this->getTotal(), 2),
];
}
}
for more detail in response,
use Illuminate\Http\Resources\Json\Resource;
class ExampleDetailResource extends Resource
{
public function toArray($request)
{
return [
"id" => $this->id,
"user" => new UserResource($this->user),
"total" => number_format($this->getTotal(), 2),
"details" => ExampleDetailsResource::collection($this->details),
];
}
}
in your index method inside controller,
return response()->json(new ExampleResource($data),200);
in your show method inside controller
return response()->json(new ExampleDetailResource($data),200);
I am trying to save some data into a Joint Table using CakePHP. This is the part of the application that I would like to fix - it is a normal BelongsToMany association with additional columns:
Model > Entity:
/* Durations */
class Duration extends Entity {
protected $_accessible = [
'duration' => true,
'cost' => true,
];
}
/* Commercials */
class Commercial extends Entity {
protected $_accessible = [
'info' => true,
'commercial_durations' => true,
];
}
/* CommercialDurations */
class CommercialDuration extends Entity {
protected $_accessible = [
'duration_id' => true,
'commercial_id' => true,
'quantity' => true,
'duration' => true,
'commercial' => true,
];
}
Model > Table:
class DurationsTable extends Table {
public function initialize(array $config)
{
$this->table('durations');
$this->displayField('id');
$this->primaryKey('id');
$this->belongsToMany('Commercials', [
'through' => 'CommercialDurations',
]);
}
}
class CommercialsTable extends Table
{
public function initialize(array $config){
$this->table('commercials');
$this->displayField('id');
$this->primaryKey('id');
$this->belongsToMany('Durations', [
'through' => 'CommercialDurations'
]);
$this->hasMany('CommercialDurations', [
'foreignKey' => 'commercial_id'
]);
}
}
class CommercialDurationsTable extends Table {
public function initialize(array $config)
{
$this->table('commercial_durations');
$this->displayField('id');
$this->primaryKey('id');
$this->belongsTo('Durations', [
'foreignKey' => 'duration_id',
'joinType' => 'INNER'
]);
$this->belongsTo('Commercials', [
'foreignKey' => 'commercial_id',
'joinType' => 'INNER'
]);
}
}
Now, I created a new View where I want people to be able to choose one Duration, type the quantity and add that value to the database. I am using the following code:
<?php
echo $this->Form->create($commercial);
echo $this->Form->input('durations._duration', ['options' => $durations]);
echo $this->Form->input('durations._joinData.quantity');
echo $this->Form->submit(__('Next'), ['class' => 'button small right', 'escape' => false]);
echo $this->Form->end()
?>
The problem with this form is that the durations select is not showing the 'duration' field from the Durations table, but instead is showing all the fields from that table (one per row) as JSON
<option value="0">{ "id": 1, "duration": "30 sec", "cost": 450 }</option>
Once I submit the form I can't save this information into the Commercials object or CommercialDurations. This is what I get from the $this->request->data object:
[
'durations' => [
'_duration' => '2',
'_joinData' => [
'quantity' => '2'
]
]
]
The output of debug((string)$commercial) before I start the form is:
/src/Template/Commercials/features.ctp (line 22)
'{
"id": 2,
"info": "AAAAAA ",
"created": "2015-04-16T21:48:48+0000",
"updated": null,
"durations": [],
}'
How can I display the data correctly on the form?
How can I retrieve and save this data?
Thanks!!
I don't get your Models relations, since according to your post a duration belongs to a commercial and a commercial belongs to a duration.
For the matter of explaining you how the request should be sent, and how the form looks like let's assume for this example that your models are like these:
Your commercial has a commercial duration and this commercial duration belongs to a duration
So your models would look like these:
Commercial has many commercial duration.
Commercial duration belongs to commercial.
Commercial duration belongs to duration.
Duration has many commercial duration.
Your add function should be like this one (assuming you are not saving a new duration, just the commercial and the commercial duration)
$commercial = $this->Commercials->newEntity($this->request->data,[
'associated' => ['Commercialdurations']
]);
if ($this->Commercials->save($commercial)) {
$success = true;
}else{
$success = false;
}
The request data should look like this:
{
"commercialdurations":{
"Commercialduration":{
"duration_id":"1",
"quantity":"1",
"duration":"1"
}
},
"info":"something"
}
Basically you are sending the data of a new Commercial (info) and the commercial durations associated to this ( you could send multiple commercial durations).
For your form to display the duration basically you have to serialize this information un the controller, go to your add action and add this. (You could use anyway you want to retrieve the data, all that matters is that you send it back to the view)
$durations = $this->Commercials->Commercialdurations->Durations->find('list', ['limit' => 200]);
$this->set(compact('commercial',durations'));
$this->set('_serialize', ['commercial']);
Then in your form you can use the data
echo $this->Form->input('duration_id', ['options' => $durations]);
So your form would look something like this:
echo $this->Form->input('info');
echo $this->Form->input('commercials.Commercial.duration_id', ['options' => $durations]);
echo $this->Form->input('commercials.Commercial.quantity');
echo $this->Form->input('commercials.Commercial.duration');
Basically you want to send a request with all the levels of associated data.
See this other question for guidence about saving associated data:
Cake PhP 3 Saving associated data in multiples levels
To see more about how to build a form:
http://book.cakephp.org/3.0/en/views/helpers/form.html#creating-inputs-for-associated-data