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');
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 have an app contain many tables :
products table , order table , order_items table.
I made models for all of them as below :
class Product extends Model
{
use HasFactory;
protected $fillable = [
'product_name',
'category_id',
'price',
'sale',
'counter',
'isfavore'
];
public function Order_item()
{
return $this->hasMany('App\Models\Order_item');
}
public function category()
{
return $this->belongsTo('App\Models\Category' ,'category_id');
}
}
class Order extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'status',
];
public function order_item()
{
return $this->hasMany('App\Models\Order_item');
}
public function user()
{
return $this->belongsTo('App\Models\User' ,'user_id');
}
}
class Order_item extends Model
{
use HasFactory;
protected $fillable = [
'order_id',
'product_id',
'quantity',
];
public function order()
{
return $this->belongsTo('App\Models\Order' ,'order_id');
}
public function product()
{
return $this->belongsTo('App\Models\Product' ,'product_id');
}
}
I want to display details like below depend on order_id :
{
"id": 7,
"user_id": 23,
"status": "ordered",
"order_item": [
{
"id": 30,
"order_id": 7,
"product_id": 5,
"quantity": 1,
"product_name" : "banana", //-----------------------want to add from product model
"price" : "2000" //-----------------------want to add from product model
},
{
"id": 31,
"order_id": 7,
"product_id": 1,
"quantity": 1,
"product_name" : "apple",
"price" : "1500"
}
]
}
I create this controller which show same result above without product name and price:
public function get_order($id){
$order=Order::find($id);
$order->order_item;
return $order;
}
Now just I need to add product name and price from product model ,How can I do that?
You have to load the relationships.
Try this:
public function get_order($id){
$order=Order::find($id);
$order_items = Order_item::with('product')->where('order_id',$id)->get();
return $order;
}
I get best answer from #Develope and #danish-khan-I ,just change controller to :
public function get_order($id){
$order=Order::with('order_item.product')->find($id);
return $order;
}
class Product extends Model
{
use HasFactory;
protected $fillable = [
'product_name',
'category_id',
'price',
'sale',
'counter',
'isfavore'
];
public function Order_item()
{
return $this->hasMany('App\Models\Order_item');
}
public function category()
{
return $this->belongsTo('App\Models\Category' ,'category_id');
}
}
class Order extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'status',
];
public function order_item()
{
return $this->hasMany('App\Models\Order_item');
}
public function user()
{
return $this->belongsTo('App\Models\User' ,'user_id');
}
}
class Order_item extends Model
{
use HasFactory;
protected $fillable = [
'order_id',
'product_id',
'quantity',
];
public function order()
{
return $this->belongsTo('App\Models\Order' ,'order_id');
}
public function product()
{
return $this->belongsTo('App\Models\Product' ,'product_id');
}
}
--- CONTROLLER ---
public function get_order($id){
$order=Order::with('order_item.product')->where('id',$id)->first();
}
---- RESPONSE ----
{
"id": 7,
"user_id": 23,
"status": "ordered",
"order_item": [
{
"id": 30,
"order_id": 7,
"product_id": 5,
"quantity": 1,
"product" : {
"product_name" : "banana",
"price" : "2000"
}
},
{
"id": 31,
"order_id": 7,
"product_id": 1,
"quantity": 1,
"product" : {
"product_name" : "banana",
"price" : "2000"
}
}
]
}
How do I return the image list from the Laravel API?
I want to send images from the API as a collection. How can I do it?
I would like to do it using ProductCollection resource if possible.
Thank you from now.
I would like to fetch the file data from this:
{
"data": [
{
"id": 20,
"name": "Cali Howe",
"price": "4.00",
"imageUrl": "C:\\Shop\\shop.api\\public\\img/default.png",
"category_name": "Consequuntur ut.",
"detail": "http://127.0.0.1:8000/api/product/20"
},
{
"id": 19,
"name": "Annie Murazik",
"price": "13.00",
"imageUrl": "C:\\Shop\\shop.api\\public\\img/default.png",
"category_name": "Consequuntur ut.",
"detail": "http://127.0.0.1:8000/api/product/19"
}
],
"links": {
"first": "http://127.0.0.1:8000/api/product?page=1",
"last": "http://127.0.0.1:8000/api/product?page=10",
"prev": null,
"next": "http://127.0.0.1:8000/api/product?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 10,
"path": "http://127.0.0.1:8000/api/product",
"per_page": 2,
"to": 2,
"total": 20
}
}
Product Controller:
public function index()
{
return ProductCollection::collection(Product::orderBy('id', 'DESC')->paginate(2));
}
Product Collection:
class ProductCollection extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'price' => $this->price,
'imageUrl' => $this->imagePath('default.png'),
'category_name' => $this->category->name,
'detail' => route('product.show', $this->id),
];
}
}
Product Model:
class Product extends Model
{
protected $guarded = ['id', 'created_at', 'updated_at'];
public function category()
{
return $this->belongsTo(Category::class);
}
public function imagePath($fileName)
{
return public_path('img/' . $fileName);
}
}
Product Migration:
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('category_id');
$table->string('name')->index();
$table->decimal('price')->default(0);
$table->string('image')->nullable();
$table->timestamps();
$table->foreign('category_id')->references('id')
->on('categories')->onDelete('cascade');
});
}
You can convert the image to base64
$imagedata = file_get_contents("/path/to/image.jpg");
$base64 = base64_encode($imagedata);
Edited
class ProductCollection extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'price' => $this->price,
'imageUrl' => $this->imagePath('default.png'),
'category_name' => $this->category->name,
'detail' => route('product.show', $this->id),
'base64' => base64_encode(file_get_contents($this->imagePath('default.png')),
];
}
}
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,
];
}
}
I have a Product Model that needs to be connected with Processings, that describe how the Product can be manipulated, and Positions, that describe where the Product can be manipulated.
In other words, a Product can be processed into multiple positions.
The "Product Data" should be returned with nested processings into positions, so that, for every Position into the database, a Product can be associated with related Processings, in a scheme similar to the one below:
Products [
product_attributes,
Positions [
position_attributes,
Processings: [
processing_attributes
]
]
]
So, I have created a 3-way Pivot Table with the following code:
Schema::create('product_position_processing', function (Blueprint $table) {
// 3-way pivot
$table->integer('product_id')->unsigned()->index();
$table->foreign('product_id', 'ppp_aid_foreign')->references('id')->on('products')->onDelete('cascade');
$table->integer('position_id')->unsigned()->index();
$table->foreign('position_id', 'ppp_bid_foreign')->references('id')->on('positions')->onDelete('cascade');
$table->integer('processing_id')->unsigned()->index();
$table->foreign('processing_id', 'ppp_cid_foreign')->references('id')->on('processings')->onDelete('cascade');
$table->primary(['product_id', 'position_id', 'processing_id'], 'index-base-combined');
// these attributes define the minimum and maximum order amount based on the product + position + processing combination
$table->integer('minimum_order_amount')->nullable();
$table->integer('maximum_order_amount')->nullable();
$table->timestamps();
});
Then, I have defined the relations between the 3 different models in this way:
Processing Model
class Processing extends Model {
use SoftDeletes;
protected $dates = ['deleted_at'];
protected $guarded = ['id', 'created_at', 'updated_at'];
protected $casts = [
'is_publicly_hidden' => 'boolean'
];
protected $hidden = ['pivot'];
public function products() {
return $this -> belongsToMany ( Product::class, 'product_position_processing', 'processing_id', 'product_id' );
}
public function positions () {
return $this -> belongsToMany ( Position::class, 'product_position_processing', 'processing_id', 'position_id' );
}
...
...
}
Position Model
class Position extends Model {
use SoftDeletes;
protected $dates = ['deleted_at'];
protected $guarded = ['id', 'created_at', 'updated_at'];
protected $hidden = ['pivot'];
public function products() {
return $this->belongsToMany ( Product::class, 'product_position_processing', 'position_id', 'product_id' );
}
public function processings ( ) {
return $this -> belongsToMany ( Processing::class, 'product_position_processing', 'position_id', 'processing_id' );
}
...
...
}
Product Model
class Product extends Model implements SluggableInterface {
use SoftDeletes;
use SluggableTrait;
protected $dates = ['deleted_at'];
protected $guarded = ['id', 'created_at', 'updated_at'];
protected $sluggable = [
'build_from' => 'name',
'save_to' => 'slug',
'include_trashed' => true
];
...
...
public function processings() {
return $this->belongsToMany(Processing::class, 'product_position_processing', 'product_id', 'processing_id');
}
public function positions() {
return $this->belongsToMany(Position::class, 'product_position_processing', 'product_id', 'position_id');
}
...
...
public function scopeWithCompleteData ($query) {
return $query->with([
...
...
'positions' => function($query) {
return $query->with(['processings' => function ( $query ) {
return $query->select('id', 'name')->groupBy('processing_id');
}])->groupBy('position_id');
},
...
...
]);
}
}
Now, the Products are returned in json via the ProductsController that, with the following code, returns a JSON in this format.
Product Getter:
...
...
$products = Product::withCompleteData() -> get();
return $this -> ok ( $products );
Example "ALL Products" #get JSON:
{
"status": "OK",
"data": [
{
"id": 1,
"category_id": 1,
"subcategory_id": 0,
"brand_id": 1,
"sku": "BD615149D6",
...
...
"positions": [
{
"id": 1,
"name": "POS1",
"processings": [
{
"id": 1,
"name": "PROC1",
"pivot": {
"position_id": 1,
"processing_id": 1
}
},
{
"id": 2,
"name": "PROC2",
"pivot": {
"position_id": 1,
"processing_id": 2
}
},
{
"id": 3,
"name": "PROC3",
"pivot": {
"position_id": 1,
"processing_id": 3
}
}
]
},
{
"id": 2,
"name": "POS2",
"created_at": "2016-08-01 07:39:11",
"updated_at": null,
"deleted_at": null,
"processings": []
},
{
"id": 3,
"name": "POS3",
"created_at": "2016-08-01 07:39:11",
"updated_at": null,
"deleted_at": null,
"processings": []
}
]
},
{
"id": 2,
"category_id": 2,
"subcategory_id": 0,
"brand_id": 2,
"sku": "BD615149D6",
...
...
"positions": [
{
"id": 5,
"name": "POS5",
"created_at": "2016-08-01 07:39:11",
"updated_at": null,
"deleted_at": null,
"processings": []
}
]
}
]
}
The problem is, the data returned is not correct. The Query Builder seems to connect the various relationships in a strange way. Unfortunately I'm still not enough proactive in understanding what lies below the magic of the query builder, and don't know how to debug and where to make modifications to the code.
Can anyone help out with this 3-way pivot table with nested objects into the Laravel query builder?