ResourceCollection is not providing a ResourceCollection
I'm trying to provide a collection of my tables to display for a Bulk (index) or all my tables in the database...
There appears to be a bug causing the ResourceCollection to not work, What should I check?
Resource\Order.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class Order extends ResourceCollection
{
/**
* Transform the resource collection into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}
ResourceCollection is returning this incorrect Json response?
// 20191017103310
// http://domain.test/api/middleware/orders/bulkindex
[
{
"data": {
"connection": {
},
"grammar": {
},
"processor": {
},
"bindings": {
"select": [
],
"from": [
],
"join": [
],
"where": [
],
"having": [
],
"order": [
],
"union": [
],
"unionOrder": [
]
},
"aggregate": null,
"columns": null,
"distinct": false,
"from": "orders",
"joins": null,
"wheres": [
],
"groups": null,
"havings": null,
"orders": null,
"limit": null,
"offset": null,
"unions": null,
"unionLimit": null,
"unionOffset": null,
"unionOrders": null,
"lock": null,
"operators": [
"=",
"<",
">",
"<=",
">=",
"<>",
"!=",
"<=>",
"like",
"like binary",
"not like",
"ilike",
"&",
"|",
"^",
"<<",
">>",
"rlike",
"not rlike",
"regexp",
"not regexp",
"~",
"~*",
"!~",
"!~*",
"similar to",
"not similar to",
"not ilike",
"~~*",
"!~~*"
],
"useWritePdo": false
}
},
{
"data": {
"connection": {
},
"grammar": {
},
"processor": {
},
"bindings": {
"select": [
],
"from": [
],
"join": [
],
...
ETC...
Please note I can change it to a JsonResource and it works as expected...
EDIT: api.php (route)
Route::get('/orders/bulkindex/', function () {
return OrderResource::collection(Order::all());
});
EDIT:
use App\Http\Resources\Cart as CartResource;
return [
'data' => $this->collection,
'carts' => CartResource::collection($this->carts), // CODE DOES NOT WORK
];
I get this error message:
Property [carts] does not exist on this collection instance.
Resources\Cart.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class Cart extends ResourceCollection
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return parent::toArray($request);
}
}
Pass an Eloquent Model collection to the constructor of the resource directly
Route::get('/orders/bulkindex/', function () {
return OrderResource(Order::all());
});
From the docs
After defining your resource collection, it may be returned from a route or controller:
use App\Http\Resources\UserCollection;
use App\User;
Route::get('/users', function () {
return new UserCollection(User::all());
});
Do not call the static method ::collection()
Hope this helps
Related
I was required to return this json, I am using api resources in Laravel:
{
"data": [
{
"type": "users",
"id": "1",
"attributes": {
"name": "test name",
"lastname": "test lastname"
"projects": 2
},
"relationships": {
"projects": {
"data": [
{
"id": 1,
"type": "projects"
}
]
}
}
}
]
}
A user has a first Project, I am doing this:
Models/User.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class User extends Model
{
use HasFactory;
protected $guarded = ['id'];
public function firstProject()
{
return $this->hasOne(Project::class)->oldest();
}
public function projects()
{
return $this->hasMany(Project::class);
}
}
ProjectCollection.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class ProjectCollection extends ResourceCollection
{
public function toArray($request)
{
return [
'data' => $this->collection
];
}
}
ProjectResource.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class ProjectResource extends JsonResource
{
public function toArray($request)
{
return [
"type" => "projects",
"id" => $this->id
];
}
}
UserCollection.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class UserCollection extends ResourceCollection
{
public function toArray($request)
{
return [
'data' => $this->collection
];
}
}
UserResource.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray($request)
{
return [
"type" => "users",
"id" => $this->id,
"attributes" => [
"name" => $this->name,
"lastname" => $this->lastname
"projects" => $this->whenCounted('projects')
],
"relationships" => [
"projects" => new ProjectCollection($this->firstProject),
]
];
}
}
UsersController.php
$users = User::with('firstProject')->withCount('projects')->latest()->limit(10)->get();
return new UserCollection($users);
If I do this:
"relationships" => [
"projects" => new ProjectCollection($this->firstProject),
]
I get this error:
Call to undefined method App\Models\Project::mapInto()
But if I change it to this:
"relationships" => [
"projects" => $this->firstProject,
]
It doesn't display errors, but I get this json:
...
...
"relationships": {
"projects":
{
"id": 1,
"type": "projects"
}
}
...
...
The projects is not wrapped in a data attribute y is not displaying the []
What can I do? Thank you.
I just neeed to wrap the $this->firstProject in an array
"relationships" => [
"projects" => new ProjectCollection([$this->firstProject]),
]
and now it is wrapping it as an array in a data attribute despite I have only one item
"relationships": {
"projects": {
"data": [
{
"id": 1,
"type": "projects"
}
]
}
}
This is the behaviour I wanted, now everything is fine
I store JSON string in a column of MySQL table. When I try to return that column data as php/json array via API it's yet as a string.
Here is my code which returns an array of questions and in every question object, I've options array as JSON stringify. But, when it returns I don't want options as stringified.
public function getQuestions($id, Request $request)
{
// return $id;
$questions = Question::where('quiz_id', $id)->orderByRaw("RAND()")->get();
$questions->makeHidden(['ans']);
// $questions->toJson(['options']);
// $questions['options'] = json_encode(questions['options']);
return $questions;
}
It returns like this.
[
{
"id": 100000008,
"quiz_id": 10000,
"title": "কোন বানানটি সঠিক?",
"options": "[\"কলসি\",\"কলসই\",\"কলশি\",\"কলশী\"]",
"answer": 1
},
{
"id": 100000009,
"quiz_id": 10000,
"title": "কোন বানানটি সঠিক?",
"options": "[\"ঠোট\",\"ঠোঁট\",\"থট\",\"কোনটি নয়\"]",
"answer": 2
}
]
But I want like this:
[
{
"id": 100000008,
"quiz_id": 10000,
"title": "কোন বানানটি সঠিক?",
"options": ["কলসি","কলসই","কলশি","কলশী"],
"answer": 1
},
{
"id": 100000009,
"quiz_id": 10000,
"title": "কোন বানানটি সঠিক?",
"options": ["ঠোট","ঠোঁট","থট","কোনটি নয়"],
"answer": 2
}
]
I just added $casts convert the string to array
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Question extends Model
{
protected $table = 'question';
protected $fillable = [
'quiz_id',
'title',
'opt1',
'opt2',
'opt3',
'opt4',
'ans',
'options',
'answer'
];
protected $casts = [
'options' => 'array',
];
public function quiz()
{
return $this->belongsTo('App\Quize', 'quiz_id' , 'id');
}
}
I need to return products of a particular category along with the following products in the subcategory of the category and I use laravel polymorphic for this work, can you help me how to return products?
I use from a recursive function for this work and this work and return to me but Not optimal
Product.php
/**
* Get all of the owning product_able models.
*/
public function product_able()
{
return $this->morphTo();
}
Category.php
/**
* Get the category's product.
*/
public function product()
{
return $this->morphOne(Product::class, 'product_able');
}
/**
* Get all of the categories for the children.
*/
public function children()
{
return $this->hasMany(self::class, 'parent_id');
}
/**
* Get the category that owns the parent.
*/
public function parent()
{
return $this->belongsTo(self::class, 'parent_id');
}
CategoryController.php
public function detail()
{
$products = new Collection();
$category = Category::ofUuid(request('category_uuid'))->ofType(config('constants.category.type.main'))->active()->first();
$products->push($category->product);
$subCategory = CategoryDetailResource::collection($category->children);
$products->push(self::check($category));
$productsFlatten = $products->flatten();
$productsFilterNull = Utility::filterNullValue($productsFlatten);
$productsPagination = Utility::paginate_collection($productsFilterNull, 15);
$productsResource = ProductResource::collection($productsPagination);
return response()->json([
'sub_categories' => $subCategory,
'products' => $productsResource
]);
}
public function check($categories)
{
$data = [];
foreach ($categories->children as $category) {
if ($category->product) {
if ($category->product->status == config('constants.product.status.active'))
$data[] = [
'product' => $category->product,
'children' => self::check($category),
];
$data[] = [
'children' => self::check($category),
];
}
}
return $data;
}
CategoryResource.php
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'uuid' => $this->uuid ?? '',
'title' => $this->describe->title ?? '',
'path' => $this->file->path ?? config('constants.default.category.image'),
'children' => CategoryDetailResource::collection($this->children()->ofType(0)->get() ?? ''),
];
}
my output:
{
"sub_categories": [
{
"uuid": "afcac5f8-95d4-42f5-af7e-b7db943c5d09",
"title": "other",
"path": "https://dkstatics-public.digikala.com/digikala-products/2326879.jpg?x-oss-process=image/resize,m_lfit,h_500,w_500/quality,q_80",
"children": [
{
"uuid": "c7b5d0f8-25af-437e-ac7e-4103c8bc1960",
"title": "cover",
"path": "https://dkstatics-public.digikala.com/digikala-products/5157933.jpg?x-oss-process=image/resize,m_lfit,h_500,w_500/quality,q_80",
"children": []
},
{
"uuid": "9cd0c171-e46d-4e1d-b3b4-5c71a79fdd39",
"title": "power",
"path": "https://dkstatics-public.digikala.com/digikala-products/834627.jpg?x-oss-process=image/resize,m_lfit,h_500,w_500/quality,q_80",
"children": []
},
{
"uuid": "f6b728a7-674f-44b8-9a8c-70d891283af7",
"title": "headphone",
"path": "https://dkstatics-public.digikala.com/digikala-products/2737272.jpg?x-oss-process=image/resize,m_lfit,h_500,w_500/quality,q_80",
"children": []
},
{
"uuid": "a8160991-9750-4473-9252-dc4b53eef62f",
"title": "holder",
"path": "https://dkstatics-public.digikala.com/digikala-products/2027918.jpg?x-oss-process=image/resize,m_lfit,h_500,w_500/quality,q_80",
"children": [
{
"uuid": "3216cd11-ba0e-431f-96b1-6665b9b6b420",
"title": "car",
"path": "https://dkstatics-public.digikala.com/digikala-products/4635214.jpg?x-oss-process=image/resize,m_lfit,h_500,w_500/quality,q_80",
"children": []
}
]
}
]
},
{
"uuid": "a017d012-313c-47d9-8146-965ba0fb4bb3",
"title": "mobile",
"path": "https://dkstatics-public.digikala.com/digikala-products/4560689.jpg?x-oss-process=image/resize,m_lfit,h_500,w_500/quality,q_80",
"children": []
},
{
"uuid": "eff6c9ff-7f1a-401b-ba90-df2b3e3b3567",
"title": "camera",
"path": "https://dkstatics-public.digikala.com/digikala-products/266661.jpg?x-oss-process=image/resize,m_lfit,h_500,w_500/quality,q_80",
"children": []
},
{
"uuid": "a935ebb0-5392-4d56-b30d-942080ae1957",
"title": "laptop",
"path": "https://dkstatics-public.digikala.com/digikala-products/5177558.jpg?x-oss-process=image/resize,m_lfit,h_500,w_500/quality,q_80",
"children": []
}
],
"products": [
null
]
}
I created method show() in controller to return name and timestamps + all permissions this role has. Spatie/Laravel-Permission is responsible for associating users with permissions and roles in my API.
public function show($name)
{
$role = Role::findByName($name);
return response()->json([
$role,
$role->permissions
]);
}
Output:
[
{
"name": "root",
"created_at": "2019-04-08 19:41:49",
"updated_at": "2019-04-08 19:41:49",
"permissions": [
{
"name": "users.store"
},
{
"name": "users.destroy"
},
{
"name": "users.show.id"
},
{
"name": "users.update.id"
},
{
"name": "users.show.name"
},
{
"name": "users.update.name"
}
]
},
[
// This is duplicate
{
"name": "users.store"
},
{
"name": "users.destroy"
},
{
"name": "users.show.id"
},
{
"name": "users.update.id"
},
{
"name": "users.show.name"
},
{
"name": "users.update.name"
}
]
]
As you can see there is useless duplicate of permissions.
But if I will remove $role from my response
return response()->json([
// $role,
$role->permissions
]);
Everything is fine
[
[
{
"name": "users.store"
},
{
"name": "users.destroy"
},
{
"name": "users.show.id"
},
{
"name": "users.update.id"
},
{
"name": "users.show.name"
},
{
"name": "users.update.name"
}
]
]
And when I want to return only $role without permissions like this:
return response()->json([
$role,
// $role->permissions
]);
My output is:
[
{
"name": "root",
"created_at": "2019-04-08 19:41:49",
"updated_at": "2019-04-08 19:41:49"
}
]
When you call $role->permissions in your response, permissions are loaded into the $role object, and then serialized to json. So basically you're sending $role object with loaded permissions, and you're also sending permissions themselves.
Try:
public function show($name)
{
$role = Role::findByName($name)->load('permissions');
return response()->json([
$role
]);
}
you can use Eloquent: API Resources here
public function show($name)
{
return new RoleResource(Role::findByName($name));
}
and RoleResource like this:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class RoleResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'name' => $this->name,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'permissions' => $this-> permissions,
];
}
}
For some bizarre reason my Laravel 5.6 app continues to return a User object with all of its relations.
My query in the Api/UserController:
public function show($user_id)
{
return User::with('meta', 'roles')->find($user_id);
}
The response:
{
"id": 1,
"name": "Admin",
"email": "admin#example.com",
"company_id": 1,
"meta": {
"id": 1,
"user_id": 1,
"laptop": 0,
"mobile": 0,
"created_at": "2018-03-07 14:58:41",
"updated_at": "2018-04-06 16:13:10"
},
"roles": [
{
"id": 2,
"name": "admin",
"label": "Admin",
"permissions": null,
"pivot": {
"user_id": 1,
"role_id": 2
}
}
],
"company": {
"id": 1,
"name": "Company",
"active": 1,
"created_at": "2018-04-12 15:06:01",
"updated_at": "2018-05-15 11:20:15",
"is_max_user_limit_reached": true
}
}
The route (inside routes/api.php):
Route::group(['middleware' => 'auth:api'], function () {
Route::resource('/users', 'Api\UserController', ['as' => 'api']);
});
User model:
namespace App\Models;
use App\Models\Role;
class User extends Authenticatable implements HasMedia
{
use HasApiTokens, Notifiable, Billable, HasMediaTrait;
protected $table = 'users';
protected $fillable = ['name', 'email', 'password', 'is_active', 'company_id', 'stripe_id', 'card_brand', 'card_last_four', 'trial_ends_at'];
protected $hidden = ['password', 'remember_token','card_brand', 'card_last_four'];
protected $appends = ['extra', 'is_staff_only', 'first_four_training_sections', 'is_free_tier', 'is_agency_tier', 'is_team_tier', 'is_enterprise_tier'];
public static $rules = [
// create rules
'name' => 'required',
'email' => 'required|email|unique:users'
];
public function meta()
{
return $this->hasOne(UserMeta::class);
}
public function company()
{
return $this->belongsTo(Company::class, 'company_id')->where('active', 1);
}
public function roles()
{
return $this->belongsToMany(Role::class);
}
public function getExtraAttribute()
{
return [
'roles' => [
'cpo' => (int)$this->hasRole('cpo'),
'ap' => (int)$this->hasRole('ap'),
'cao' => (int)$this->hasRole('cao'),
]
];
}
public function getIsStaffOnlyAttribute()
{
if($this->roles->count() == 1 && $this->hasRole('staff')) {
return true;
}
return false;
}
public function getIsFreeTierAttribute()
{
return $this->company->subscription_tier == 0;
}
public function getIsAgencyTierAttribute()
{
return $this->company->subscription_tier == 1;
}
public function getIsTeamTierAttribute()
{
return $this->company->subscription_tier == 2;
}
public function getIsEnterpriseTierAttribute()
{
return $this->company->subscription_tier == 3;
}
public function getFirstFourTrainingSectionsAttribute() {
return UserTrainingSection::where('user_id', $this->id)->orderBy('id')->take(4)->get();
}
}
This is very strange behavior. I am asking for only the roles and meta related data but it's always returning every single relation on the User model.
Even if I try User::find($user_id); it will still return all the relations.
Anyone know what's going on here?
I'm using Laravel 5.6 and PHP 7.2
You can use the $hidden property to remove relationships from the response:
protected $hidden = ['company', ...];
You can also temporarily remove relationships:
$user->addHidden('company');