1

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",
        ],
    },
}
Mithc
  • 851
  • 8
  • 23
  • You can add pluck method on relationship function. I mean just add pluck on meta() function in user model after hasMany function. Im not sure but i think it's work. and see this post [Get Specific Columns Using “With()” Function in Laravel Eloquent](https://stackoverflow.com/questions/19852927/get-specific-columns-using-with-function-in-laravel-eloquent) – Tirdad Abbasi Jul 28 '20 at 00:28
  • @Tirdad Abassi that's a good approach but I'm using the relationship to add those meta values (`$user->meta()->create([...])`, so adding pluck to the method will potentially result in errors. I know I can create them with `UserMeta::create([...])`, but the app uses the other way already. – Mithc Jul 28 '20 at 00:41
  • Can you create another relationship ? like something pluckedMeta() and add pluck function. I know it's not best solution but I think it's will work! :D – Tirdad Abbasi Jul 28 '20 at 00:46

0 Answers0