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",
],
},
}