333

I have two tables, User and Post. One User can have many posts and one post belongs to only one user.

In my User model I have a hasMany relation...

public function post(){
    return $this->hasmany('post');
}

And in my post model I have a belongsTo relation...

public function user(){
    return $this->belongsTo('user');
}

Now I want to join these two tables using Eloquent with() but want specific columns from the second table. I know I can use the Query Builder but I don't want to.

When in the Post model I write...

public function getAllPosts() {
    return Post::with('user')->get();
}

It runs the following queries...

select * from `posts`
select * from `users` where `users`.`id` in (<1>, <2>)

But what I want is...

select * from `posts`
select id,username from `users` where `users`.`id` in (<1>, <2>)

When I use...

Post::with('user')->get(array('columns'....));

It only returns the column from the first table. I want specific columns using with() from the second table. How can I do that?

miken32
  • 42,008
  • 16
  • 111
  • 154
Awais Qarni
  • 17,492
  • 24
  • 75
  • 137

20 Answers20

558

Well I found the solution. It can be done one by passing a closure function in with() as second index of array like

Post::query()
    ->with(['user' => function ($query) {
        $query->select('id', 'username');
    }])
    ->get()

It will only select id and username from other table. I hope this will help others.


Remember that the primary key (id in this case) needs to be the first param in the $query->select() to actually retrieve the necessary results.*

ManojKiran A
  • 5,896
  • 4
  • 30
  • 43
Awais Qarni
  • 17,492
  • 24
  • 75
  • 137
  • 5
    That's odd, I couldn't get this to work. As soon as I added in `$query->select('id','username');`, I was getting `Trying to get property of non-object` – user1669496 Nov 12 '13 at 16:49
  • @user1669496 Do you have defined relations `hasmany` and `belongsto` in your model?? – Awais Qarni Nov 13 '13 at 06:01
  • 7
    Weird! still returns all the fields of the user. @AwaisQarni – justin Jan 07 '14 at 07:51
  • @justin share your query – Awais Qarni Jan 07 '14 at 09:39
  • @AwaisQarni i will give you the laravel forum [click here](http://forums.laravel.io/viewtopic.php?pid=67637#p67637) – justin Jan 09 '14 at 06:07
  • 3
    Thank you for sharing this. Where did you discover this possibility? I didn't find this in the Laravel docs. – Symen Timmermans Apr 02 '14 at 14:28
  • @SymenTimmermans Just trying different possibilities of Eloquent. :-) – Awais Qarni Apr 03 '14 at 04:49
  • 137
    For those who see this, remember that the primary key (`id` in this case) is necessary in the `$query->select()` to actually retrieve the necessary results. I omitted this in my code and couldn't figure out why it wouldn't find any results. Silly me! Hope this helps anyone else with the same problem – Azirius Apr 14 '14 at 16:10
  • This is a very basic question, but how do I refer to a column from the second table if: $result = Post::with('user')->find(1), then how do I retrieve the user id? ---- $result->user.id ? – Jorge Dec 13 '14 at 03:37
  • @jasa You can refer to a column from the second table ('users' in this example) like this: $result->user->id; – Amr Jan 17 '15 at 05:54
  • @SymenTimmermans it's under http://laravel.com/docs/4.2/eloquent#eager-loading constraints. the API docs are pretty poor describing these parameters, and the examples are sometimes hard to find amongst the tutorial style documentation. (I much prefer reference style...) – scipilot Feb 14 '15 at 05:20
  • 1
    I love this solution! I get trouble when I need to get a device detail by using specific device_id, and device activity in specific start time and end time, it works perfectly as I need! – Fenix Lam Oct 11 '17 at 08:05
  • 1
    You can do it with a single command in Laravel 5 see my answer https://stackoverflow.com/a/47238258/2311074 – Adam Nov 15 '17 at 14:20
  • 11
    great, just need to add foreign key, over here its post_id otherwise result will be empty. Mentioning Foreign key is important here. Thanks :) – Hashaam Ahmed Nov 28 '18 at 07:04
  • 3
    Just like @HashaamAhmed I needed to include the foreign key too. If I don't, nothing is returned. – Clemorphy Nov 26 '19 at 14:06
  • It is good but, this keys also sent in API response in JSON how to avoid – Lokendra Parihar Apr 26 '21 at 12:36
  • 1
    Important!!! needs to include the foreign key (assuming it is user_id in this example). Otherwise it will show null array. – Qazi Ammar Sep 27 '22 at 10:26
283

You can do it like this since Laravel 5.5:

Post::with('user:id,username')->get();

Care for the id field and foreign keys as stated in the docs:

When using this feature, you should always include the id column and any relevant foreign key columns in the list of columns you wish to retrieve.

For example, if the user belongs to a team and has a team_id as a foreign key column, then $post->user->team is empty if you don't specifiy team_id

Post::with('user:id,username,team_id')->get();

Also, if the user belongs to the post (i.e. there is a column post_id in the users table), then you need to specify it like this:

Post::with('user:id,username,post_id')->get();

Otherwise $post->user will be empty.

Adam
  • 25,960
  • 22
  • 158
  • 247
  • 43
    Don't forget to include the foreign key (assuming it is post_id here) to resolve the relationship, otherwise you will get empty results for your relation. – Hashaam Ahmed Nov 28 '18 at 07:03
  • 5
    This should really be the selected answer. Works like a charm :) – Graham Oct 09 '19 at 21:09
  • @Adam, This will restrict columns in the child table, how can I restrict columns from table Parent table along with child table? – Sodium Dec 05 '19 at 00:28
  • 1
    @GauravGenius everything you want to restrict from parant belogns in the `get()` method `Post::with('user:id,username')->get(['age', 'color']);` – Adam Dec 05 '19 at 05:13
  • @Kamlesh `Post::with(['user:id,username,team_id', 'othertable:id,horse'])` – Adam Jun 03 '21 at 06:35
  • @Adam its weird answer for me because I want to fetch all the records of user table and need 2 fields of other table. I do not want to write all the fields in query as you suggested in your answer. Thanks dear. – Kamlesh Jun 03 '21 at 12:14
  • @Kamlesh then go with `Post::with(['user', 'othertable:id,horse'])` – Adam Jun 03 '21 at 12:27
  • @Adam `Post::with('user:id,username')->get(['age', 'color']); ` returns me post with age and color columns but user object is null – Imran_Developer Dec 15 '21 at 09:17
  • 1
    @Imran_Developer whats the primary key on your users table? Maybe `Post::with('user:id,username')->get(['age', 'color', 'id']);` works? – Adam Dec 15 '21 at 12:05
  • yes, basically "user_id" was missing. It works now: `Post::with('user:id,username')->get(['age', 'color', 'user_id']);` – Imran_Developer Dec 15 '21 at 12:37
  • I find this as a better solution, but, note that if you have space in between like this: `Post::with('user:id, username, post_id')->get();` it will throw error. – Platinum Aug 08 '23 at 01:48
  • Also, this doesn't work on hasManyThrough or hasOneThrough relationships – Platinum Aug 12 '23 at 09:52
115

For loading models with specific column, though not eager loading, you could:

In your Post model

public function user()
{
    return $this->belongsTo('User')->select(['id', 'username']);
}

Original credit goes to Laravel Eager Loading - Load only specific columns

Supun Praneeth
  • 3,087
  • 2
  • 30
  • 33
user1669496
  • 32,176
  • 9
  • 73
  • 65
  • 25
    But this relation will make it just like hardcoded. In all condition it will always return me these two fields. It may happen that I need more fields in some other situations – Awais Qarni Nov 11 '13 at 05:25
  • Then you must use the query builder. – user1669496 Nov 11 '13 at 16:16
  • 7
    This should be the accepted answer. This is the correct way to do it. – BugHunterUK Jan 16 '17 at 14:22
  • 1
    Is there a way to do this in the Laravel 5.3 version? I think they changed it... again.... and now it returns null when I try with this approach – Andre F. Feb 05 '17 at 08:09
  • It's been a while, but you could add a `$fields` parameter. – JordyvD Feb 25 '17 at 23:22
  • @g3mini could you help write an answer on how that would work calling like `$model->user` without invocation? – danronmoon Apr 12 '17 at 16:17
  • Without invocation? So, `$model->relation->get(['field1', 'field2']);` That should work – JordyvD Apr 13 '17 at 06:40
  • It is not Good idea because if any day if you want full column then you can not get form this. every time you have to modify this. – pankaj Mar 25 '20 at 08:27
  • can I put specific conditions like this **return $this->belongsTo('User')->select(['id', 'username'])->where('id', '=', 1);** or something like this – Shailendra Jun 05 '21 at 11:02
102

When going the other way (hasMany):

User::with(['post'=>function($query){
    $query->select('id','user_id');
}])->get();

Don't forget to include the foreign key (assuming it is user_id in this example) to resolve the relationship, otherwise you'll get zero results for your relation.

Douwe de Haan
  • 6,247
  • 1
  • 30
  • 45
Thijs
  • 1,121
  • 1
  • 7
  • 2
46

In Laravel 5.7 you can call specific field like this

$users = App\Book::with('author:id,name')->get();

It is important to add foreign_key field in the selection.

Shreyansh Panchal
  • 827
  • 12
  • 22
hendra1
  • 1,359
  • 1
  • 15
  • 24
  • 21
    don't forget to add the foreign key field – hendra1 Feb 27 '18 at 05:00
  • 5
    Dont forget to notice @hendra1's comment, all foreign key fields are also necessary along with the primary key, otherwise you will get blank collection. Dude saved my time. Thanks – Rajesh Vishnani Oct 22 '18 at 07:37
28

If you want to get specific columns using with() in laravel eloquent then you can use code as below which is originally answered by @Adam in his answer here in response of this same question, the answer's main code is as below :

Post::with('user:id,username')->get();

So i have used it in my code but it was giving me error of 1052: Column 'id' in field list is ambiguous, so if you guys are also facing same problem

Then for solving it you have to specify table name before the id column in with() method as below code:

Post::with('user:user.id,username')->get();
Haritsinh Gohil
  • 5,818
  • 48
  • 50
  • This returns null for me but i didn't know this was possible before! Ok, so I posted prematurley but I did find a result. To use this method (looks much cleaner) you must include the relationship link, for example the ID column when pulling the with data. Without this specifier, you get a null return. – Adsy2010 Jun 16 '20 at 15:27
  • yeah @Adsy2010, for getting related relationship data you also have to get id column or the primary key or whichever id is responsible for that relationship. – Haritsinh Gohil Jun 17 '20 at 08:16
  • @Kamlesh `Post::with('user:user.id,username')->get();` this code will return all fields of post model or table and only `id` and `username` field from the `users` table but keep in mind before using `with()` function you should have relationship to relevant table in your model, if you don't have relationship then you can use [join](https://laravel.com/docs/8.x/queries#joins) from the query builder. – Haritsinh Gohil Jun 03 '21 at 08:11
24

I came across this issue but with a second layer of related objects. @Awais Qarni's answer holds up with the inclusion of the appropriate foreign key in the nested select statement. Just as an id is required in the first nested select statement to reference the related model, the foreign key is required to reference the second degree of related models; in this example the Company model.

Post::with(['user' => function ($query) {
        $query->select('id','company_id', 'username');
    }, 'user.company' => function ($query) {
        $query->select('id', 'name');
    }])->get();

Additionally, if you want to select specific columns from the Post model you would need to include the user_id column in the select statement in order to reference it.

Post::with(['user' => function ($query) {
        $query->select('id', 'username');
    }])
    ->select('title', 'content', 'user_id')
    ->get();
jwarshaw
  • 259
  • 2
  • 7
16

In your Post model:

public function userWithName()
{
    return $this->belongsTo('User')->select(array('id', 'first_name', 'last_name'));
}

Now you can use $post->userWithName

Chris Harrison
  • 5,512
  • 3
  • 28
  • 36
Duy Hoang
  • 532
  • 1
  • 4
  • 11
7

There is another alternative you can eager load specific columns

public function show(Post $post)
{
    $posts = $post->has('user')->with('user:id,name,email,picture')->findOrFail($post->id);
    return view('your_blade_file_path',compact('posts);
}

In your Post model you should have user relationship also

public function user()
{
    return $this->belongsTo( User::class, 'user_id')->withDefault();
}

Note: It is mentioned in Laravel docs.

https://laravel.com/docs/8.x/eloquent-relationships#eager-loading-specific-columns

Maik Lowrey
  • 15,957
  • 6
  • 40
  • 79
Neeraj Tangariya
  • 1,159
  • 1
  • 17
  • 29
  • can you help with this, it returns me product name but not "user" object: $product = Product::where('id', $p_id)->with('user:id,name')->get(['name']); – Imran_Developer Dec 15 '21 at 09:10
  • @Imran_Developer because you returning name only in get(['name']). do this $product = Product::with('user:id,name')->where('id', $p_id)->get(); – Neeraj Tangariya Dec 15 '21 at 11:40
  • yes dear @Neeraj , but it returns me user object with all columns of product instead of 1 or 2. but I have solved this by using: `$product = Product::with('user:id,name')->where('id', $p_id)->select('title', 'content', 'user_id')->get();` . Here "user_id" is important for reference. – Imran_Developer Dec 15 '21 at 12:00
  • @Imran_Developer ok great. – Neeraj Tangariya Dec 16 '21 at 07:34
4

Note that if you only need one column from the table then using 'lists' is quite nice. In my case i am retrieving a user's favourite articles but i only want the article id's:

$favourites = $user->favourites->lists('id');

Returns an array of ids, eg:

Array
(
    [0] => 3
    [1] => 7
    [2] => 8
)
Bilal Ahmed
  • 4,005
  • 3
  • 22
  • 42
omar j
  • 521
  • 6
  • 8
  • it return a collection! – Giovanni Far Dec 08 '15 at 21:33
  • if you want an array then call `toArray()` !!! `$user->favourites->lists('id')->toArray();` – Giovanni Far Dec 08 '15 at 21:34
  • The query will still get the other columns because the list() method just changes the results array ,so if you just need the 'id' from that table you may want to specify it in the query. It is always a good habit to keep performance in mind when doing queries. Enjoy coding! – ErvTheDev Mar 11 '16 at 09:50
  • -1 `$user->favourites` will be return `Collection` with all fields selecteds. The correct use is: `$user->favourites()->lists('id')`. – Wallace Vizerra Jan 27 '17 at 10:35
  • Selecting everything to later use PHP filtering is a bad idea. It is best to use only the needed fields in Query. It's important to know the difference between `Query\Builder::lists` method and `Collection::lists` method. – Wallace Vizerra Jan 27 '17 at 10:40
3

If you use PHP 7.4 or later you can also do it using arrow function so it looks cleaner:

Post::with(['user' => fn ($query) => $query->select('id','username')])->get();
Kornel
  • 4,184
  • 4
  • 28
  • 30
2

I faced the same issue while using belongsToMany relationship with my user model (Laravel 8.x.x).

After a long search and trial and test method. I found out this answer

You have to make sure you are selecting the id's and any foreign keys that would be needed for the relationship from either side of that relationship. This allows Eloquent to match up parents to their children.

Original credit goes to https://stackoverflow.com/a/64233242/1551102

So I included

Groups::select('groupid')
...

And it worked like a charm. Although now I want to know how to hide the groupid field after fetching. I know I can simply loop through the array and remove it. But is there any other method? potentially a simpler and better one.

Lonare
  • 3,581
  • 1
  • 41
  • 45
1

You can also specify columns on related model at the time of accessing it.

Post::first()->user()->get(['columns....']);

Shreyansh Panchal
  • 827
  • 12
  • 22
1

You can try this code . It is tested in laravel 6 version.

Controller code
 public function getSection(Request $request)
{

  Section::with(['sectionType' => function($q) {
      $q->select('id', 'name');
  }])->where('position',1)->orderBy('serial_no', 'asc')->get(['id','name','','description']);
  return response()->json($getSection);
}
Model code
public function sectionType(){
    return $this->belongsTo(Section_Type::class, 'type_id');
}
Mobarak Hossen
  • 767
  • 5
  • 7
1

Be careful that if you don't add the key column(s) it won't return anything. If you want to show only the username without the id you could instead define the $visible/$hidden properties within the Model, like so:

app/Models/User.php

protected $visible = ['username'];

Then it will retrieve only username column with:

Post::with('user')->get();

Hiding the key columns:

Alternatively you could hide the key column(s) and then retrieve only the columns you wish.

app/Models/User.php

protected $hidden = ['id'];

Specify which columns you want including the key or else it won't return anything, but this will actually only return the username, because id is $hidden.

Post::with('user:id,username')->get();
caiohamamura
  • 2,260
  • 21
  • 23
0

Now you can use the pluckmethod on a Collection instance:

This will return only the uuid attribute of the Post model

App\Models\User::find(2)->posts->pluck('uuid')
=> Illuminate\Support\Collection {#983
     all: [
       "1",
       "2",
       "3",
     ],
   }
Giovanni Far
  • 1,623
  • 6
  • 23
  • 37
0

Try with conditions.

$id = 1;
Post::with(array('user'=>function($query) use ($id){
    $query->where('id','=',$id);
    $query->select('id','username');
}))->get();
Douwe de Haan
  • 6,247
  • 1
  • 30
  • 45
Shirjeel Ahmed Khan
  • 257
  • 1
  • 5
  • 12
0

So, similar to other solutions here is mine:

// For example you have this relation defined with "user()" method
public function user()
{
    return $this->belongsTo('User');
}
// Just make another one defined with "user_frontend()" method
public function user_frontend()
{
    return $this->belongsTo('User')->select(array('id', 'username'));
}

// Then use it later like this
$thing = new Thing();
$thing->with('user_frontend');

// This way, you get only id and username, 
// and if you want all fields you can do this

$thing = new Thing();
$thing->with('user');
TomoMiha
  • 1,218
  • 1
  • 14
  • 12
0

Sometimes, when creating a small application based on (a subset of) an existing database, it may be convenient to have all models fetch only the fields you need; even when loaded by with(). It may ease debugging and avoid confusion, to name an advantage. The '$visible' attribute (mentioned above) can only be used when the model is converted to array or JSON; see the Laravel docs. Otherwise, you could use a combination of local scopes and a 'public static $fields' property, as in the simplified 'User Class' example below.

class User extends Model
{
    public static $fields = [ 'id', 'full_name', 'image' ];
    
    public function scopeFields(Builder $query) : void {
        $query->select(self::$fields);
    }
    
    public function posts(): hasMany {
        return $this->hasMany(Post::class)->select(Post::$fields);
    }
}

Let's use the well-known class Post too:

class Post extends Model
{
    public static $fields = [ 'id', 'user_id', 'text', 'created_at' ];
    
    public function scopeFields(Builder $query) : void {
        $query->select(self::$fields);
    }

    public function user(): HasOne {
        return $this->hasOne(User::class)->select(User::$fields);
    }
}

As you can see, the functions scopeFields are identical, so you should put that in an in-between class. Anyway, after this you can use the models like:

User::fields()->with('posts')->get();
Post::fields()->with('user')->get();

All the returned objects will only contain the attributes that are listed in $fields. Tested with Laravel 10.

ekkev
  • 46
  • 4
-4
EmployeeGatePassStatus::with('user:id,name')->get();
kush
  • 595
  • 5
  • 7
  • 3
    This is the same syntax as in [this answer](https://stackoverflow.com/a/47238258/7404943) (just different names for model and column) – barbsan May 15 '19 at 06:39