My actual Question is: How to return attributes to the client which I actually need, throw the rest away and be as efficient as possible?
So, I have a construction where I basically have media objects, which can be of different types, like video, picture, link and so on. This is constructed in a polymorphic manner. In this example, users can have an avatar, which is in fact a media Object, but the only thing that I'm interested in on client side is actually the image url.
Our applicaiton is RESTful and if I want to list all users, I want the users JSON to basically return a property with only the url of the avatar and not the complete polymorphic model, which I then would have to traverse on client side, not to mention the huge overhead to send from the server to the client.
From User Model:
/**
* A user can have an avatar
*
* @return \Illuminate\Database\Eloquent\Relations\MorphOne
*/
public function avatar() {
return $this->morphOne('Fanlete\Media', 'appendable');
}
From Media Model:
/**
* A media element can be extended in different ways (kind of inheritance), this function morphs to the specific implementation
*
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function mediable()
{
return $this->morphTo();
}
/**
* A media element can be appended by various models, i.e. a user to save its avatar
*
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function appendable() {
return $this->morphTo();
}
From Picture Model, a concrete Media implementation:
public function media()
{
return $this->morphMany('Fanlete\Media', 'mediable');
}
So, let's say I have a function to list all users in my controller:
public function index()
{
return User::all();
}
Where I want an output like this:
{
"id": 1,
"email": "[email protected]",
"name": "Example",
"surname": "User",
"created_at": "2015-06-17 07:05:26",
"updated_at": "2015-06-17 07:05:26",
"avatar": "http://upload.wikimedia.org/wikipedia/en/3/38/Avatarjakeneytiri.jpg"
}
At the moment I solve it like this, which feels actually not that elegant and somehow very wrong and bottlenecked:
User Model again:
protected $appends = ['avatar'];
public function getAvatarAttribute()
{
return $this->avatar()->get()->first()->mediable()->get()->first()->url;
}
Of course I could also do something like this in my controller:
$users = Users::with("avatar.mediable")->get()->all();
// Now sort out attributes I don't need and return array
and without sortin the output would look like this (very messy and huge overhead):
{
"id": 1,
"email": "[email protected]",
"name": "Example",
"surname": "User",
"created_at": "2015-06-17 07:05:26",
"updated_at": "2015-06-17 07:05:26",
"avatar":
{
"id": 6,
"appendable_id": 1,
"appendable_type": "Fanlete\\User",
"mediable_id": 6,
"mediable_type": "Fanlete\\Picture",
"created_at": "2015-06-17 07:05:26",
"updated_at": "2015-06-17 07:05:26",
"mediable":
{
"id": 6,
"url": "http://upload.wikimedia.org/wikipedia/en/3/38/Avatarjakeneytiri.jpg",
"title": null,
"created_at": "2015-06-17 07:05:26",
"updated_at": "2015-06-17 07:05:26"
}
}
}
But this would also feel like a workaround and is exactly that overhead I do not need and want.
thx
You might be almost there.
Your getAvatarAttribute()
is way too complicated, indeed. You could try to do something like:
/** we hide the relation from the json */
protected $hidden = ['avatar'];
/** we append the url of the avatar to the json */
protected $appends = ['avatar_url'];
/**
* To not be confused with the avatar() method, I used avatar_url.
*/
public function getAvatarUrlAttribute()
{
return $this->avatar->mediable->url;
}
And then, you should be able to do $users = User::with('avatar.mediable')->get();
without the bottleneck, and have a nice JSON response.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With