47

I am trying to eager load a model in laravel but only return certain columns. I do not want the whole eager loaded table being presented.

public function car()
{
    return $this->hasOne('Car', 'id')->get(['emailid','name']);
}

I am getting the following error:

log.ERROR: exception 'Symfony\Component\Debug\Exception\FatalErrorException' with message 'Call to undefined method Illuminate\Database\Eloquent\Collection::getAndResetWheres()'

ipengineer
  • 3,107
  • 7
  • 33
  • 37

11 Answers11

77

Make use of the select() method:

public function car() {
    return $this->hasOne('Car', 'id')->select(['owner_id', 'emailid', 'name']);
}

Note: Remember to add the columns assigned to the foreign key matching both tables. For instance, in my example, I assumed a Owner has a Car, meaning that the columns assigned to the foreign key would be something like owners.id = cars.owner_id, so I had to add owner_id to the list of selected columns;

rmobis
  • 26,129
  • 8
  • 64
  • 65
  • 4
    Unfortunately thats not working for me. If I add the select() method the relationship is not added. If I remove it I get the entire row. – ipengineer Jun 08 '13 at 01:36
  • 12
    Oh, you probably need the foreign key being used as well. For instance, if it'd `id`, you'd need to add `id` to the `select()`'ed columns. – rmobis Jun 08 '13 at 01:39
  • 2
    I've just updated the original answer, check it now. Basically, if `Owner -> Car` relationship is set on `owners.id -> cars.owner_id`, you have to add `owner_id` to the `select()`'ed columns list. – rmobis Jun 08 '13 at 02:27
  • 2
    Spent hours on this, and never thought about having to select the id aswell. But it makes perfectly good sense now. Thanks, i upvoted! – jah Nov 20 '13 at 11:45
  • what if i want to hide the foreign key from result? – StealthTrails Sep 04 '16 at 15:33
  • @AdamNick you'll have to filter it before displaying. There's a property that automatically does it for models, I believe it's called `guarded`. – rmobis Sep 04 '16 at 15:39
  • i figured it, it was hidden property on the model. – StealthTrails Sep 04 '16 at 19:28
  • 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:03
28

Also you don't need to specify getting specific columns in the model and the relationship method itself... You can do that whenever you need it... Like this:

$owners = Owner::
          with([
              'car' => function($q)
               {
                    $q->select('id', 'owner_id', 'emailid', 'name');
               },
               'bike' => function($q)
               {
                    $q->select('id', 'owner_id', 'emailid', 'name');
               }
          ])->
          get();

In this way you can also get all of the columns of the related model if you have ever needed to.

Popnoodles
  • 28,090
  • 2
  • 45
  • 53
Ali
  • 1,268
  • 1
  • 13
  • 20
  • 1
    I'd like to add that this works for `$q->where()` as well if you want to eager load rows with columns that match certain constraints. – WhyAyala Apr 03 '17 at 18:07
  • 1
    You can also add nested relationships like `Owner::with(['car' =>... , 'car.drivers' => ...])` and they will be loaded and structured properly! – ecdani Aug 07 '19 at 08:07
20

In your controller you should be doing something like

App\Car::with('owner:id,name,email')->get();

Supposing that you have two models defined like below

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;

class Car extends Model
{
    protected $table = 'car';

    public function owner()
    {
        return $this->belongsTo('App\Owner', 'owner_id');
    }
}

and

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;

class Owner extends Model
{
    protected $table = 'owner';

    public function car()
    {
        return $this->hasMany('App\Car', 'owner_id');
    }
}

and you have two tables something like:

owners: id | name | email | phone | other_columns...

and

cars: id | owner_id | make | color | other_columns...

Credits go to the docs: eloquent-relationships#eager-loading scroll to Eager Loading Specific Columns

Bogdan
  • 1,840
  • 1
  • 25
  • 39
11

The easiest thing you can do is go by the documentation and do the following thing for getting only specific columns without any extra line of code or closure. Follow steps,

public function car(){
    return $this->hasOne('Car', 'id');
}

then when you eager load the relation select only selected columns

$owner = $this->owner
->where('id', 23)
->with('car:id,owner_id,emailid,name')   //no space after comma(, ) and id has to be selected otherwise it will give null

Hope this works, have a great day.

Shahrukh Anwar
  • 2,544
  • 1
  • 24
  • 24
  • 2
    This is what i have been looking for! The Laravel docs give NO hint of this syntax, and i only discovered it while debugging why my eagers wouldn't load. The `id` note was the only thing i was missing. You think they'd inject it if it were missing, or at least document its existence. THANK YOU! (Correction: it's documented, but not in the version i'm stuck on - 5.3. Thanks also to @Bogdan for that additional note.) – cautionbug Sep 24 '20 at 18:55
  • 1
    Initially I thought it was a bug to always take `id` too in eagers even if we don't want `id` column. But then I realize that it is rather necessary to prevent the `ambiguous` id error of SQL, due to which SQL don't understand which `id` column you were talking about during `joins`. @cautionbug – Shahrukh Anwar Oct 16 '20 at 05:13
  • In your comment, you mentioned that there should be no space after a comma (,). This insight has helped me understand my mistake. Thank you. – Irfan Ullah Aug 25 '23 at 12:41
5

The answers from shahrukh-anwar and Bogdan are both excellent and led me to solving my version of this problem.

However, there's one critical piece i would add for clarification (even the docs don't mention it).

Take the following, which was still broken for me:

Car::with('owner:id,name,email')->get(['year', 'vin']); 

You rarely see specific column selection on the primary model (->get(...)) so it's easy to forget: your selection needs the foreign key column:

Car::with('owner:id,name,email')->get(['owner_id', 'year', 'vin']);

Sure, it seems obvious once you make the mental connection between with using a Closure and this syntax, but still, easy to overlook.

When you have tables with 30+ columns and only need 3 of them, this might keep your memory load down a bit.

cautionbug
  • 435
  • 5
  • 18
1

It is also possible on eager loading to specify a specific column to load. Suppose you have this modal class

class user extends Model {
  public function car()
    {
      return $this->hasOne('Car', 'id')->get(['emailid','name']);
    }
}

if you want to load user with its car you can do this on your controller

User::with(['car:id,emailid,name'])->get(); 

:-Note that DON"T forget to add the foreign key inside the columns you want to get with other wise it will now work. You need a car model as well as the reverse relationship.

For more details you can refer this

Eager Loading Specific Columns

Cosmin Staicu
  • 1,809
  • 2
  • 20
  • 27
1

Using load is even more easy. If we have a model already instantiated we can put for example having a model User that has a relation of one to many with model Comments and we only want to select from Comment the id and the title

In User model the relation method is

public function comments()
    {
        return $this->hasMany(Comment::class);
    }

In Comment model the relation method is

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

In our controller if we want to recover user with their comments

$user = new User();

$user->load('comment:id,title')

And we will get the user with comment relation loaded only with id and title :-)

0

Try WITH() & WHERE() conditions.

$user_id = 1; // for example

Post::with(array('user'=>function($query) use ($user_id ){
            $query->where('user_id','=',$user_id );
            $query->select('user_id','username');
        }))->get();
Shirjeel Ahmed Khan
  • 257
  • 1
  • 5
  • 12
0

Sometimes you need to make your relation generic so that can call on your ease.

public function car()
{
    return $this->hasOne('Car', 'id');
}

and while fetching you can mention columns you need.

$owners = Owner::with(['car' => function($query) { // eager loading
            $query->select('emailid','name');
           }
          ])->get();

foreach($owners as $owner){
    $owner->car->emailid;
    $owner->car->name;
}
Tayyab Hussain
  • 1,658
  • 1
  • 18
  • 21
0

make sure to put id column when using eager loading

Voucher::with(['storeInfo:id,name as branchName,code as branchCode'])->get();

IN MODEL

public function storeInfo() { return $this->belongsTo(Branch::class,'branch_id'); }

Muhammad Ibrahim
  • 507
  • 9
  • 19
0

For Nested Relation, we can use this

 Post::with(['user' => function ($query) {
            $query->select('id','company_id', 'username');
        }, 'user.company' => function ($query) {
            $query->select('id', 'name');
        }])->get();
Ahmad
  • 1
  • 1
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 15 '22 at 12:20