When querying models with relationship, is it possible to run pluck()
so I only get certain fields of the relationship?
Use case
A user database, and users can have custom fields; these fields are considered meta
stored in a separate user_meta
table (User
hasMany
UserMeta
).
Goal
When running a query like:
User::with('meta')->find(1)
I get a model like:
App\Models\User {#4048
id: 1,
name: "...",
...
meta: Illuminate\Database\Eloquent\Collection {#4038
all: [
App\Models\UserMeta {#4053
id: ...,
user_id: ...,
key: "last_opened",
value: "2020-07-30",
...
},
],
},
}
All fields of the meta models are important during queries, but for most of the time I need only two fields: key
and value
.
Attempts
Model Appends
First, I tried with $appends
on the model to include a custom relationship structure, resulting in exactly what I need.
So, when calling:
User::find(1)
I get this:
App\Models\User {#4048
id: 1,
name: "...",
...
meta: array [
"last_opened": "2020-07-30",
],
}
The problem? Appended on every query even when not needed. Even though I could dynamically append it, it will require trickery, and I prefer "include when needed" rather than "exclude when not needed".
Hidden Model Attributes
Then, I tried using $hidden
in the UserMeta
model, so I can hide everything but the key
and value
fields. It works, not exactly what I want but it works.
So, this:
User::with('meta')->find(1)
Results in this:
App\Models\User {#4048
id: 1,
name: "...",
...
meta: Illuminate\Database\Eloquent\Collection {#4038
all: [
App\Models\UserMeta {#4053
key: "last_opened",
value: "2020-07-30",
},
],
},
}
I can live with the above solution not a big deal, so at this point is mere curiosity and study case. Nonetheless, the above results in a slightly trickier way to reach properties in the APIs (like, instead of directly targetting user.meta.last_opened
I'd have to search for the index where the value is equals to last_opened
).
Is there a way to call pluck('value', 'key')
on with('meta')
to have a key => value
result in the relationship node in the model? That way, I can have the structure I need, being able to include it only when I need it, and all in the same query.
Hypothetically (with wrong syntax of course), something like this:
User::(with('meta')->pluck('value', 'key'))->find(1)
To get something like this:
App\Models\User {#4048
id: 1,
name: "...",
...
meta: Illuminate\Database\Eloquent\Collection {#4038
all: [
"last_opened": "2020-07-30",
],
},
}