I am trying to rename my data wrapper for the resource I am fetching using Laravel resource. I read in the documentation here how you are supposed to do it, so I did:
ScanResource.php
class ScanResource extends JsonResource
{
public static $wrap = 'scan';
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
//return parent::toArray($request);
return ['id' => $this->id,
'rftag' => $this->rftag,
'jc_number' => $this->jc_number,
'station' => $this->station,
'agent' => $this->agent,
'created_at' => $this->created_at->format('d/m/Y H:i:s'),
'updated_at' => $this->updated_at->format('d/m/Y'),];
}
}
AppServiceProvider.php
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
//Paginator::useBootstrapThree();
Schema::defaultStringLength(191);
//JsonResource::withoutWrapping();
//Resource::withoutWrapping();
ScanResource::withoutWrapping();
}
public function register()
{
//
}
}
This is how I am trying to fetch the resource in my controller:
public function show($id)
{
$product = ScanDetail::find($id);
if (is_null($product)) {
return $this->sendError('Scan details not found.');
}
return $this->sendResponse(new ScanResource($product), 'Scan info retrieved successfully.');
}
currently I am getting the following JSON with Postman:
{
"success": true,
"data": {
"id": 1,
"rftag": "E200203204205212165166",
"jc_number": "15",
"station": "Repairing",
"agent": "kbailey",
"created_at": "11/06/2020 01:29:53",
"updated_at": "11/06/2020"
},
"message": "Scan info retrieved successfully."
}
But I want:
{
"success": true,
"scan": {
"id": 1,
"rftag": "E200203204205212165166",
"jc_number": "15",
"station": "Repairing",
"agent": "kbailey",
"created_at": "11/06/2020 01:29:53",
"updated_at": "11/06/2020"
},
"message": "Scan info retrieved successfully."
}
I tried this, this, this, this and this. This is not what I am using, so I did not think it would work. I also tried modifying my toArray to:
return [
'scan'=>['id' => $this->id,
'rftag' => $this->rftag,
'jc_number' => $this->jc_number,
'station' => $this->station,
'agent' => $this->agent,
'created_at' => $this->created_at->format('d/m/Y H:i:s'),
'updated_at' => $this->updated_at->format('d/m/Y'),]
];
but its giving the following JSON:
{
"success": true,
"data": {
"scan": {
"id": 1,
"rftag": "E200203204205212165166",
"jc_number": "15",
"station": "Repairing",
"agent": "kbailey",
"created_at": "11/06/2020 01:29:53",
"updated_at": "11/06/2020"
}
},
"message": "Scan info retrieved successfully."
}
Again, not what I want since I will be fetching different resources from the database using api calls. So I want to customize the outer wrapper. Any assistance is/will be greatly appreciated. Thanks in advance.
You don't need to do anything in your AppServiceProvider.php
You just need to create ScanResource.php as below:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class ScanResource extends JsonResource
{
/**
* The "data" wrapper that should be applied.
*
* #var string
*/
public static $wrap = 'scan';
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
return [
'id' => $this->id,
'rftag' => $this->rftag,
'jc_number' => $this->jc_number,
'station' => $this->station,
'agent' => $this->agent,
'created_at' => $this->created_at->format('d/m/Y H:i:s'),
'updated_at' => $this->updated_at->format('d/m/Y')
];
}
public function with($request)
{
return [
"success" => true,
"message": "Scan info retrieved successfully."
];
}
}
And you need to use that ScanResource.php in the ScanController.php as below
// use the namespace at the top
use App\Http\Resources\UserResource;
// add the below action in the controller.
public function show($id)
{
$scan= Scan::find($id);
return new ScanResource($scan);
}
I have tested the code in my postman and I got below output
{
"scan": {
"id" : 7,
"rftag" : "abcde",
"jc_number" :"AB123FG" ,
"station" : "abc",
"agent" : "Stanton Satterfield",
"created_at": "29/10/2021 05:18:42",
"updated_at": "29/10/2021 05:18:42"
},
"success" : true,
"message": "Scan info retrieved successfully."
}
in your controller change to this
return ['scan' => YourResource::collection(YourModel::get())];
and in your AppServiceProvider.php file add this
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Http\Resources\Json\JsonResource; // this
class AppServiceProvider extends ServiceProvider
{
public function register()
{
JsonResource::withoutWrapping(); // and this
}
}
hope it's work for you :)
Related
I was used relationships in my model and it's working fine but the sort of output array is wrong.I want to move created_at and updated_at in the end of the array
The output is:
{
"id": 1,
"title": "Test home",
"created_at": "2022-01-01T15:27:31.000000Z", <---------
"updated_at": "2022-01-01T15:27:31.000000Z", <---------
"doors": [
{
"id": 1,
"home_id": 1,
"button_count": 2,
"created_at": null,
"updated_at": null
}
]
}
But it's should be:
{
"id": 1,
"title": "Test home",
"doors": [
{
"id": 1,
"home_id": 1,
"button_count": 2,
"created_at": null,
"updated_at": null
}
],
"created_at": "2022-01-01T15:27:31.000000Z", <---------
"updated_at": "2022-01-01T15:27:31.000000Z" <---------
}
And it's my model class:
class Home extends Model
{
use HasFactory;
protected $table = "homes";
protected $hidden = ["device_id"];
protected $with = ["doors"];
public function device(){
return $this->hasOne(Device::class,"id","device_id");
}
}
The cleanest approach if you are building an API (not sure if your scenario) is by using Resources where you can format the response as you wish. This is also helpful for being sure that Models will be returned with the same structure in all endpoints.
Assuming you want to fetch all homes with their devices, you can create a DeviceResource for returning specific device properties
PHP artisan make:resource DeviceResource
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class DeviceResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
return [
'id' => $this->id,
'home_id' => $this->home_id,
'button_count' => $this->button_count,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}
}
Then create a HomeResource which will includes DeviceResource
PHP artisan make:resource HomeResource
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class HomeResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'devices' => DeviceResource::collection($this->whenLoaded('devices')),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}
}
Then you just return the HomeResource collection in your controller
$homes = Home::with('devices')->get();
$response = HomeResource::collection($homes);
return $response;
Or in case you want to return only 1 Home...
$home = Home::with('devices')->findOrFail($home_id);
$response = new HomeResource($home);
return $response;
Be aware that if you don't load devices relationships, the HomeResource won't return the devices property.
I used laravel resources for my api responses and added paginate method. Upon using paginate method I always get a result like this where laravel by default gives three keys namely "data", "links" and "meta". But I want to change the resource to my own need.
{
"data": [
{
"id": 1,
"name": "Eladio Schroeder Sr.",
"email": "therese28#example.com",
},
{
"id": 2,
"name": "Liliana Mayert",
"email": "evandervort#example.com",
}
],
"links":{
"first": "http://example.com/pagination?page=1",
"last": "http://example.com/pagination?page=1",
"prev": null,
"next": null
},
"meta":{
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "http://example.com/pagination",
"per_page": 15,
"to": 10,
"total": 10
}
}
But I want a result like this
{
"data": [
{
"id": 1,
"name": "Eladio Schroeder Sr.",
"email": "therese28#example.com",
},
{
"id": 2,
"name": "Liliana Mayert",
"email": "evandervort#example.com",
}
],
"metadata": {
"pagination": {
"offset": 50,
"limit": 25,
"previousOffset": 25,
"nextOffset": 75,
"currentPage": 3,
"pageCount": 40,
"totalCount": 1000
}
}
}
How can I be able to achieve this. I am using Laravel 7.*
My controller code:
public function index(Request $request)
{
return DiscussionResource::collection($this->discussion->getDiscussionList($request));
}
My Model method looks like this:
public function getDiscussionList($request){
return $this->ofSearch($request)
->orderBy('created_at', config('settings.pagination.order_by'))
->paginate(config('settings.pagination.per_page'));
}
My resource looks like this:
class DiscussionResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'question_id' => $this->question_id,
'user_id' => $this->user_id,
'user_image' => $this->user->userProfile->image,
'user_role' => $this->user->type,
'comment' => $this->comment,
'is_pinned' => $this->is_pinned,
'created_at' => $this->created_at->toDateString()
];
}
}
There are so many ways to do that in laravel and here you go 2 ways of it:
First Way: you can create a custom PaginatedResourceResponse and override the default paginationLinks. for example like below:
use Illuminate\Http\Resources\Json\PaginatedResourceResponse;
class CustomPaginatedResourceResponse extends PaginatedResourceResponse
{
protected function paginationLinks($paginated)
{
return [
'prev' => $paginated['prev_page_url'] ?? null,
'next' => $paginated['next_page_url'] ?? null,
];
}
protected function meta($paginated)
{
$metaData = parent::meta($paginated);
return [
'current_page' => $metaData['current_page'] ?? null,
'total_items' => $metaData['total'] ?? null,
'per_page' => $metaData['per_page'] ?? null,
'total_pages' => $metaData['total'] ?? null,
];
}
}
then override toResponse method(Actually, the toResponse method converts resource collection to responses)
public function toResponse($request)
{
return $this->resource instanceof AbstractPaginator
? (new CustomPaginatedResourceResponse($this))->toResponse($request)
: parent::toResponse($request);
}
You may consider overriding other methods if you want to customize your response furthermore.
Second Way: you can just override the toResponse method in the ResourceCollection and make it as you wish!
More Ways HERE!
If you want to customize the metadata you can take the help of the with() method that comes with laravel for collections.
// in DiscussionResource file
public function with($request)
{
return [
'meta' => [
'key' => 'value',
],
];
}
If you want to customize it from controller you may do something like this
return (DiscussionResource::collection($this->discussion->getDiscussionList($request)))
->additional(['meta' => [
'key' => 'value',
]]);
And in case you want it for a single resource you can modify it at the toArray() method
public function toArray($request)
{
return [
'data' => $this->collection,
'links' => [
'self' => 'link-value',
],
];
}
For more details you can check this out https://laravel.com/docs/7.x/eloquent-resources#adding-meta-data
I'm using FormRequest to validate, also overriding messages function to set custom messages.
Somehow, I can't get integer-array validation to work. This is where I'm at:
This is what my request should look like:
// ACCEPTED:
{
"name": "my name",
"items": [20, 7, 1, 2]
}
// REJECTED
{
"name": "Meine neue Kampagne",
"items": ["ANY STRING HERE", 1, 2]
}
So I want my items to only be an array of integers. These are my rules, which are fully working:
return [
'name' => 'required|max:191',
'items' => 'array',
'items.*' => 'int|exists:jobpostings,id'
];
So far, I tried the following (all found in online tutorials):
return [
'items.*.int' => "item is not int",
];
return [
'items.*[
'int' => "item is not int"
]
];
They all just gave the common "The items.0 must be an integer." message.
Can anyone help me maybe?
Here is the complete FormRequest:
<?php
namespace App\Http\Requests\Campaign;
use Illuminate\Foundation\Http\FormRequest;
class CreateCampaignRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name' => 'required|max:191',
'items' => 'array',
'items.*' => 'int|exists:items,id'
];
}
public function messages()
{
return [
'name.required' => __('campaign.createCampaign.name_required'),
'name.max' => __('campaign.createCampaign.name_max'),
'items.array' => __('campaign.createCampaign.items_array'),
'items.*.int' => __('campaign.createCampaign.items_*_int'),
'items.*.exists' => __('campaign.createCampaign.items_*_exists'),
];
}
}
I'm creating an backend with multiple stores. Each stores with their own products
I've created a store and a product and am trying to resource the products to all the stores.
When I try add my products to my store in StoreResource.php
using:
'products' => ProductResource::collection($this->products),
I get the error:
Call to a member function first() on string
I've looked on line for numerous explanations and tutorials, but get met with the same error
* STORE MODEL *
public function products()
{
return $this->hasMany(Product::class);
}
*STORE CONTROLLER *
public function index()
{
return StoreResource::collection(Store::with('products')->paginate(5));
}
STORE Resource
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class StoreResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'image' => $this->image,
'created_at' => (string) $this->created_at,
'updated_at' => (string) $this->updated_at,
'products' => ProductResource::collection($this->products),
];
}
}
* Product MODEL *
public function stores()
{
return $this->belongsTo(Store::class);
}
*Product CONTROLLER *
public function index()
{
return ProductResource::collection(Product::with('stores')->paginate(5));
}
Product Resource
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class Purchaseresource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'productName' => $this->productName,
'amount' => $this->amount,
'total_product_price' => $this->total_product_price,
'week' => $this->week,
'day' => $this->day,
'month' => $this->month,
'year' => $this->year,
'created_at' => (string) $this->created_at,
'updated_at' => (string) $this->updated_at,
];
}
}
What I expect is to get a relation between my store and products so they are displayed as this in the API repsonse
{
"id": 0,
"name": "Store",
"image": "image",
"products": [
{
"id": 1,
"name": "product",
"price": 7,
"qty": 100,
"total": 0
},
]
}
So the product would become nested in the store.
you first need to make a query in your controller like:
$query = Store::all();
and after something like:
$stores = StoreResource::collection($query);
and in your Resource you can pass:
'product' => $this->product
in the resource array
I am trying to push an item status into array and then return through API Resource, but API resource returning error. I am trying following script to do that
$Games = Game::all();
return new GameResource($Games);
And it is return as following
{
"data": [
{
"id": 1,
"name": "similique",
"type_id": 3,
"created_at": "2018-10-30 11:23:27",
"updated_at": "2018-10-30 11:23:27"
}
]
}
I am trying following to achieve my desire json array
$Games = Game::all();
$DataArray = ['status' => 'success', 'data' =>$Games ];
return new GameResource($DataArray);
But it is returning error
Call to a member function toBase() on array in fil
My desire json array is following
{
"status": "success",
"data": [
{
"id": 1,
"name": "similique",
"type_id": 3,
"created_at": "2018-10-30 11:23:27",
"updated_at": "2018-10-30 11:23:27"
}
]
}
In your GameResource.php change the toArray() method:
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
'status' => 'success', // Here goes the logic which checks for success or failure. However, this depends on what you consider as "success".
];
}
You can try this code:
$games = Game::get();
return response()->json(new GameResource($games));
data is a public property of your GameResource ? then your code should be
return ['status' => 'success', 'data' => (new GameResource($Games))->data ];
Brother, I am using this way.
Step 1: I create a generalOutput Resource.php and modify like below.
class GeneralOutputResource extends JsonResource
{
private $status;
private $data;
public function __construct($status, $data)
{
$this->status = $status;
$this->data = $data;
}
public function toArray($request)
{
return ['status' => $this->status, 'data' => $this->data];
}
}
Step 2:
Then I just call the class like this
$a_user = User::find(1);
return new GeneralOutputResource(1, $a_user);
Everytime I return the response, I will call the GeneralOutputResponse.