11

I have the following code in my controller where I am trying to load all 'members'. Every member can have more than one phone number. Phone numbers are stored in a table called phone_numbers and are tied to user id's. Below, I am trying to load the last phone number stored for every member. Note, the user has a hasMany relationship on the phone.

User.php

public function phone()
{
    return $this->hasMany('App\Model\PhoneNumber');
}

This is what I tried:

$members = \App\Model\User::all();
$members->load('phone');

I appreciate any suggestions on how to accomplish this.

AnchovyLegend
  • 12,139
  • 38
  • 147
  • 231

2 Answers2

29

To get the latest row, you can just use latest, and use hasOne relationship like this:

public function phone()
{
    return $this->hasOne('App\Model\PhoneNumber')->latest();
}

So you can get the latest phone for all users:

User::with('phone')->get();
TsaiKoga
  • 12,914
  • 2
  • 19
  • 28
  • You should mention that `with('phone')` before retrieving models is important because of relationship's lazy loading. (I forgot it in my answer) – Shizzen83 Apr 04 '20 at 13:29
  • Thanks for the reply. This is clean. Will this also get users without a phone number stored? – AnchovyLegend Apr 04 '20 at 13:31
  • Wait, this doesn't work. Note, phone is **hasMany** relationship. – AnchovyLegend Apr 04 '20 at 13:31
  • @AnchovyLegend yes, if you want to get without phone number, you can use `whereHas` method. You can define `hasMany phones` and `hasOne phone` – TsaiKoga Apr 04 '20 at 13:32
  • No, I want it to include all users with and without phone numbers and get the phone numbers when available. So I can then do `$mebmers->phone_number` to get the latest phone number in my view. – AnchovyLegend Apr 04 '20 at 13:33
  • @Shizzen83 In fact, the post `load()` is eager-loading too. – TsaiKoga Apr 04 '20 at 13:34
  • @TsaiKoga, what do you mean I can have `hasMany phones` and `hasOne phone` ? – AnchovyLegend Apr 04 '20 at 13:34
  • @AnchovyLegend You can defined two relationship. One is `hasMany phones` which can load all phones for u. Another relationship `hasOne phone`, which can load the latest phone for u. And you can use one of them if you need it. – TsaiKoga Apr 04 '20 at 13:42
  • I don't understand how I would do that. I would have two separate phone relationship functions in my User entity? – AnchovyLegend Apr 04 '20 at 13:45
  • @AnchovyLegend Yes. Use diff method name. Laravel will convert the diff relationship's methods to the corresponding raw sql. Get the diff results. – TsaiKoga Apr 04 '20 at 13:49
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/210939/discussion-between-anchovylegend-and-tsaikoga). – AnchovyLegend Apr 04 '20 at 13:54
-1

You could achieve this by doing:

in your User model:

/**
     * The accessors to append to the model's array form.
     *
     * @var array
     */
    protected $appends = ['last_phone'];


protected function getLastPhoneAttribute()
{
    return $this->phone()->orderBy('created_at')->first();
}
use App\Model\User;

$lastPhones = User::all()->map->last_phone;
Shizzen83
  • 3,325
  • 3
  • 12
  • 32
  • What I want to add it to my members data that I am going to inject? so I can do `$member->phone_numeber`? – AnchovyLegend Apr 04 '20 at 13:22
  • Note, this doesn't work. I set $appends to the actual field name of the phone number i.e. `phone_number` and in my controller I did this: `$members = User::all()->map->phone_number; dd($members);` All phone numbers are coming back `null`. – AnchovyLegend Apr 04 '20 at 13:24
  • 1
    Be careful about accessor, this mean every query to this User model, it will all load the phones – TsaiKoga Apr 04 '20 at 13:25
  • `last_phone` is a virtual attribute, the `$appends` property tells Laravel to append it on every model. – Shizzen83 Apr 04 '20 at 13:26
  • Okay, what is the purpose of `getLastPhoneAttribute`? how does `map` know what to do? – AnchovyLegend Apr 04 '20 at 13:27
  • 1
    You have everything explained here https://laravel.com/docs/7.x/eloquent-mutators for the attribute and here for the `map` https://laravel.com/docs/7.x/collections#higher-order-messages – Shizzen83 Apr 04 '20 at 13:31
  • Like @TsaiKoga said, notice that use `-attribute()` (accessor) will produce database query every time. If you use this expression within iteration, it will cause performance issue like N+1 query. Better use `with()` (eager loading) instead. – Benyi Apr 09 '21 at 05:29