Reputation: 14844
I'm writing a REST API using Lumen. I have for my example 2 models User
and Post
. Post
model use the method belongsTo
to get the User
model which created this post. My goal was to define an accessor so I can get the author's username of the post just like that Post::find($id)->author
. So according to the doc I do this:
Post.php :
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $table = 'posts';
protected $appends = ['author'];
protected $fillable = [
'title',
'description'
];
protected $hidden = [
'user_id',
'created_at',
'updated_at'
];
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
public function getAuthorAttribute()
{
return $this->user->username;
}
}
Now the getter works well and I can easily get the author of the given Post
.
But if I tried to return the Post
in a JSON response, it's also return me weird attributes like user
that seems to come from my user()
method that call a belongsTo()
:
return response()->json(Post::find(2), 200);
{
"id": 2,
"title": "Amazing Post",
"description": "Nice post",
"author": "FooBar",
"user": {
"id": 4,
"username": "FooBar"
}
}
If I use the attributesToArray()
it's work as expected:
return response()->json(Post::find(2)->attributesToArray(), 200);
{
"id": 2,
"title": "Amazing Post",
"description": "Nice post",
"author": "FooBar"
}
Moreover if I remove the getter getAuthorAttribute()
and the $appends
declaration, I don't get the unexpected user
attribute.
But I don't want to use this method each time and it doesn't make it work if I want to return all my Post
using:
return response()->json(Post::all(), 200);
Have someone an idea why I get this additional attribute using belongsTo
?
Upvotes: 0
Views: 592
Reputation: 46
Your first approach was good, you just need to add 'user' into the $hidden array
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $table = 'posts';
protected $appends = ['author'];
protected $fillable = [
'title',
'description'
];
protected $hidden = [
'user_id',
'created_at',
'updated_at',
'user', // <-- add 'user' here
];
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
public function getAuthorAttribute()
{
return $this->user->username;
}
}
And your resulting model would be:
{
"id": 2,
"title": "Amazing Post",
"description": "Nice post",
"author": "FooBar"
}
Upvotes: 0
Reputation: 6518
This behavior is because of performance.
When you call $post->user
for first time, The Laravel read it from the database and keep it in $post->relation[]
for next usage. So next time Laravel can read it from the array and prevent from executing a query again(it will be useful if you use it in multiple places).
Plus, the user is also an attribute and the Laravel merges
$attributes
and $relations
array together when you call $model->toJson()
or $model->toArray()
The Laravel's Model source code:
public function toArray() { return array_merge($this->attributesToArray(), $this->relationsToArray()); } public function jsonSerialize() { return $this->toArray(); }
Upvotes: 1