I'm trying to building API Resource and I wanna hide in collection the relationship attribute.
For example, I want to hide attribute 'permissions' only in RoleCollection. I mean I just only wanna hide this attribute in Collection, not Resource. Because Collection be called from Resource but I don't want to hide it in Resource.
Role.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class Role extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'created_at' => $this->created_at->format('Y-m-d H:i:s'),
'updated_at' => $this->updated_at->format('Y-m-d H:i:s'),
'permissions' => Permission::collection($this->permissions),
];
}
}
RoleCollection.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class RoleCollection extends ResourceCollection
{
public function toArray($request)
{
return parent::toArray($request);
}
}
RoleController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Spatie\Permission\Models\Role;
use App\Http\Resources\Role as RoleResource;
use App\Http\Resources\RoleCollection;
class RoleController extends Controller
{
public function index()
{
$resource = Role::paginate();
return new RoleCollection($resource);
}
public function show($id)
{
$resource = Role::with('permissions')->find($id);
return new RoleResource($resource);
}
}
Response from: api/role/1
{
"data": {
"id": 1,
"name": "Super Administrador",
"created_at": "2019-05-07 16:45:38",
"updated_at": "2019-05-07 16:45:38",
"permissions": [
{
"id": 1,
"name": "user.list"
},
{
"id": 2,
"name": "user.view"
},
{
"id": 3,
"name": "user.save"
},
{
"id": 4,
"name": "user.delete"
}
]
}
}
Response from : /api/roles
{
"data": [
{
"id": 1,
"name": "Super Administrador",
"created_at": "2019-05-07 16:45:38",
"updated_at": "2019-05-07 16:45:38",
"permissions": [
{
"id": 1,
"name": "user.list"
},
{
"id": 2,
"name": "user.view"
},
{
"id": 3,
"name": "user.save"
},
{
"id": 4,
"name": "user.delete"
}
]
},
{
"id": 2,
"name": "Administrador",
"created_at": "2019-05-07 16:45:38",
"updated_at": "2019-05-07 16:45:38",
"permissions": []
}
],
"links": {
"first": "http://127.0.0.1:32773/api/roles?page=1",
"last": "http://127.0.0.1:32773/api/roles?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "http://127.0.0.1:32773/api/roles",
"per_page": 15,
"to": 2,
"total": 2
}
}
One way to do this is to use the whenLoaded method. The whenLoaded will return a MissingValue instance when a relationship has not been loaded. Laravel in turn will exclude this property from your resonse.
It is not only useful for hiding properties in certain responses, but also helps with performance. Currently your resource will do a query to fetch Permission models for every Role when this relationship was not loaded.
Your resource could look like:
return [
...
'permissions' => Permission::collection($this->whenLoaded('permissions')),
];
Laravel docs
One of the best way to handle this stuff is to use Fractals.
You can define a Transformer class for all your model, handle whether to include relationships or not every time you call them, and you can also define what attributes to show or to hide.
Basically you have one and only one point where your model can be serialized in JSON and you put all your logic there.
Also you can JSON-ize whole collections, through the single model transformers, very handy!
Related
I have tables with below relationship
And my HousingAdvertisement model has
public function nearPlaces()
{
return $this->hasMany(HousingAdNearPlace::class);
}
and HousingAdNearPlace
public function nearPlace()
{
return $this->hasOne(NearPlace::class, 'id');
}
when I make query like this:
HousingAdvertisement::with('nearPlaces.nearPlace')->where('user_id', '=', auth()->user()->id)->get();
I got HousingAdNearPlace object in HousingAdvertisement model:
[...
{
...,
"near_places": [
{
"id": 27,
"housing_advertisement_id": 48,
"near_place_id": 3,
"created_at": "2021-06-29T12:23:35.000000Z",
"updated_at": "2021-06-29T12:23:35.000000Z",
"near_place": null
},
{
"id": 28,
"housing_advertisement_id": 48,
"near_place_id": 4,
"created_at": "2021-06-29T12:23:35.000000Z",
"updated_at": "2021-06-29T12:23:35.000000Z",
"near_place": null
}
]
...]
How can I got self NearPlace model like this:
[...
{
...,
"near_places": [
{
"id": 3,
"name": "Park",
"slug": "park",
"created_at": "2021-06-29T06:25:57.000000Z",
"updated_at": "2021-06-29T06:25:57.000000Z"
},
{
"id": 4,
"name": "Beach",
"slug": "beach",
"created_at": "2021-06-29T06:25:57.000000Z",
"updated_at": "2021-06-29T06:25:57.000000Z"
}
]
...]
You need "Has Many Through" relationship on HousingAdvertisement
public function nearPlaces()
{
return $this->hasManyThrough(NearPlace::class, HousingAdNearPlace::class);
}
And also define id keys as in ducumentation: https://laravel.com/docs/8.x/eloquent-relationships#has-many-through-key-conventions
You need to use laravel Model Accessor for generating slug from HousingAdvertisement's name row.
For that use -
public function slug()
{
return Str::lower($this->name);
}
This will make a slug from name attribute. Add it on your HousingAdvertisement model. Now you need to cast it with query builder.
For that use -
protected $casts = [
'slug' => 'string',
];
Add this on your HousingAdvertisement model. Finally you can query your database like -
HousingAdvertisement::find(auth()->user()->id)->get(['id', 'name', 'slug', 'created_at', 'updated_at']);
I have a many to many relation with table users,items and the pivot table user_item and i need to call the query : Select * from user_item where user_id=$user->id in laravel and return the results in json format. I try with
$user=User::find(session('user_id'))->items()->get();
return response ()->json($user);
But it doesn't work. How can I do that?
class User extends Authenticatable {
public function items (){
return $this->belongsToMany ("App\Models\Item", "user_item", "user", "item");
}
}
class Item extends Models {
public function users (){
return $this->belongsToMany ("App\Models\User", "user_item", "item", "user");
}
}
You can define columns in your pivote table using withpivot method
public function items (){
return $this->belongsToMany ("App\Models\Item", "user_item",
"user", "item")->withPivot(['column1', 'column2','another_column']);
}
to get relation instead of using get(), you should use like below:
$user=User::find(session('user_id'))->items;
return response ()->json($user);
above will give below json result:
[{
"id": 4,
"name": "PC",
"pivot": {
"column1": 1,
"column2": 4,
"another_column": "2016-03-03"
}
},
{
"id": 5,
"name": "Phone",
"pivot": {
"column1": 1,
"column2": 4,
"another_column": "2016-03-03"
}
}]
you can also include pivot in use user model using with():
$user=User::with('items')->find(session('user_id'));
return response ()->json($user);
give json result something like:
{
"id": 1,
"name": "User Name",
"email": "email#user.com",
"created_at": null,
"updated_at": null,
"items": [{
"id": 4,
"name": "PC",
"pivot": {
"column1": 1,
"column2": 4,
"another_column": "2016-03-03"
}
},
{
"id": 5,
"name": "Phone",
"pivot": {
"column1": 1,
"column2": 4,
"another_column": "2016-03-03"
}
}]
}]
I use Laravel 6.x and below is my response JSON.
{
"data": [
{
"id": 1,
"name": "quam",
"parent_id": 0
},
{
"id": 2,
"name": "quia",
"parent_id": 1
},
{
"id": 3,
"name": "beatae",
"parent_id": 1
},
{
"id": 4,
"name": "aut",
"parent_id": 2
},
{
"id": 5,
"name": "provident",
"parent_id": 0
},
{
"id": 6,
"name": "voluptate",
"parent_id": 0
},
{
"id": 7,
"name": "vel",
"parent_id": 2
},
{
"id": 8,
"name": "sed",
"parent_id": 3
},
{
"id": 9,
"name": "voluptates",
"parent_id": 0
},
{
"id": 10,
"name": "adipisci",
"parent_id": 6
},
...
]
}
But it want to be like this:
{
"data": [
{
"id": 1,
"name": "quam",
"children": [
{
"id": 2,
"name": "quam"
"children":[
{
"id": 4,
"name": "aut"
},
{
"id": 7,
"name": "vel",
"children": [
...
]
}
]
},
{
"id": 3,
"name": "quam",
"children":[
{
"id": 8,
"name": "sed"
}
]
},
]
},
{
"id": 5,
"name": "provident"
},
{
"id": 6,
"name": "voluptate",
"children": [
{
"id": 10,
"name": "adipisci"
}
]
},
{
"id": 9,
"name": "voluptates"
},
...
}
In fact, I want to remove the parent_id attribute and add children array to each object that consists of other objects have this parent_id.
CategoryResource.php
class CategoryResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'parent_id' => $this->parent_id,
];
}
}
CategoryController.php
class CategoryController extends Controller
{
public function index()
{
return CategoryResource::collection(Category::all());
}
}
How can I implement this structure?
From what I see your problem is just the relations. To create a "tree resource" you have to load a lot of relations.
IMHO it's not a good choice, expecially if you have to load a lot of elements but generally, structures like these may be a dangerous bottleneck.
Anyway... The easy way it's the eager loading, so you have to add your base model with this attribute (have a look at the official documentation)
class Parent extends Model {
// [...]
/**
* The relationships that should always be loaded.
*
* #var array
*/
protected $with = ['children'];
// [...]
public function children() {
return $this->hasMany('whatever');
}
}
next you have to update your JSON Resource as follows (also for this, have a look at the official documentation about resource relationships).
class CategoryResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'parent_it' => $this->parent_id,
'childrend' => ChildrenResource::collection($this->whenLoaded('children')),
];
}
}
In this way, since everytime you request a Parent it will eager load its children, the resource will recursively map into a Child resource each relation down to the leaf.
Assuming Category is an eloquent model, Model's can reference themselves in relationships and those relationships can be recursive.
class Category extends Model
{
protected $hidden = ['parent_id'];
public function children()
{
return $this->hasMany('App\Category', 'parent_id')->with('children');
}
}
So now getting the structure you want is as simple as
Category::with('children:id,name,parent_id')->get('id', 'name', 'parent_id');
You have to include the parent_id in the select statement in order for the relationships to work but the $hidden variable I added to the model keeps parent_id from showing up in serialized results. The only caveat here is that all categories will have a children property, which will be empty for Categories that don't have children. So in your toArray method you will have to check for empty children[] and exclude them
First you need to define a relation for retrieving children of the main category which has no parents with this method
/**
* get sub product categories of this category
*
* #return mixed
*/
public function childCategories()
{
return $this->hasMany(Category::class,'parent_id');
}
Then you need to load the children of children categories with this method :
/**
* get recursive all sub categories of this category.
*
* #return mixed
*/
public function childrenCategories()
{
return $this->hasMany(Category::class,'parent_id')->with('childCategories');
}
Now you can retrieve them with this static function
/**
* get all Main categories with children.
*
* #return mixed
*/
public static function allMainCategoriesWithChildren()
{
return self::whereNull('parent_id')->with('childrenCategories')->get();
}
now you can use it in your resource or return it in controller directly
use App\Category;
return Category::allMainCategoriesWithChildren();
I need help to populate this kind of pagination on laravel eloquent.
{
"meta": {
"count": 10,
"total": 100
},
"links": {
"first": "http://localhost/page[limit]=10&page[offset]=0",
"last": "http://localhost/page[limit]=10&page[offset]=10",
"next": "http://localhost/page[limit]=10&page[offset]=10",
"prev": "null"
},
"data": [
{
"type": "checklists",
"id": "1"
}
]
}
I have tried this code on Laravel Eloquent.
$data = Model::select('type','id')->paginate(10);
return response()->json(
[
'data' => $data
],200
);
But it shows different format, there is no META and LINKS schema on data populated.
{
"data": {
"current_page": 1,
"data": [
{
"type": "Mechanical Equipment Sales Representative",
"id": 1
}
],
"first_page_url": "http://localhost?page=1",
"from": 1,
"last_page": 4,
"last_page_url": "http://localhost?page=4",
"next_page_url": "http://localhost?page=2",
"path": "http://localhost",
"per_page": 10,
"prev_page_url": null,
"to": 10,
"total": 39
}
}
How to do that? Please help?
You can use API resources: https://laravel.com/docs/eloquent-resources#pagination
Create a collection resource:
php artisan make:resource ModelCollection
Use it in your controller:
$data = Model::select('type','id')->paginate(10);
return new ModelCollection($data);
For Lumen, create a app/Http/Resources/ModelCollection.php file:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class ModelCollection extends ResourceCollection
{
/**
* Transform the resource collection into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return parent::toArray($request);
}
}
I have a User model that relates to a Section model through a pivot model UserTrainingSection. I have a pivot table that stores the foreign keys for both tables called section_user.
I have 2 additional pivot table columns called completed and completed_date.
The problem I am having is that when I fetch my data it returns all the columns from the User model along with the additional pivot columns.
class Section extends Model
{
public $table = "sections";
public $fillable = [
'id',
'name',
'description',
'parent',
'position',
'completion_percentage'
];
public function users()
{
return $this->belongsToMany(User::class, 'section_user')->using('App\Models\UserTrainingSection')->withPivot('completed', 'completed_date');
}
}
In my API service I fetch my data like this:
Section::with(['users' => function ($q) {
$q->where('users.id', Auth::user()->id);
}])->first();
How do I only return the pivot table columns and exclude the columns from the user table?
At the moment it returns something like this:
"sections": [
{
"id": 2,
"name": "Subsection 1 training",
"description": null,
"parent": 1,
"position": 2,
"completion_percentage": null,
"created_at": "2018-05-04 09:54:09",
"updated_at": "2018-05-11 09:14:59",
"users": [
{
"id": 1,
"name": "Test",
"email": "test#test.com",
"created_at": "12-04-2018 14:51:42",
"updated_at": "2018-04-19 14:14:36",
"pivot": {
"section_id": 2,
"user_id": 1,
"completed": 1,
"completed_date": "31/05/2018",
"expires": "31/05/2019"
},
}
]
}
]
What I would like to return is something like this:
"sections": [
{
"id": 2,
"name": "Subsection 1 training",
"description": null,
"parent": 1,
"position": 2,
"completion_percentage": null,
"created_at": "2018-05-04 09:54:09",
"updated_at": "2018-05-11 09:14:59",
"users": [
{
"pivot": {
"section_id": 2,
"user_id": 1,
"completed": 1,
"completed_date": "31/05/2018",
"expires": "31/05/2019"
}
}
]
}
]
So I basically get rid of the user data I don't want and only return the pivot data.
As per your expected output i guess you need data from junction model only, If that is the case i suggest you to define direct mapping of Section and UserTrainingSection
class Section extends Model
{
public function training_users()
{
return $this->hasMany('App\Models\UserTrainingSection', 'section_id');
}
public function users()
{
return $this->belongsToMany(User::class, 'section_user')->using('App\Models\UserTrainingSection')->withPivot('completed', 'completed_date');
}
}
In query you can simply do
Section::with('training_users')->first();
In your with statement you are fetching all User data by definition.
You can add a select statement to only get the results you want. You can add that either in the relationship in your model, or where you access the model in your API.
Example 1:
Section::with(['users' => function ($q) {
$q->where('users.id', Auth::user()->id)->select('completed', 'completed_date');
}])->first();
Example 2:
public function users()
{
return $this->belongsToMany(User::class, 'section_user')->using('App\Models\UserTrainingSection')->withPivot('completed', 'completed_date')->select('completed', 'completed_date');
}
(Not tested)
See the documentation