1

My question is regarding ensuring a unique array of users in a related model using Eloquent's query builder.

One feature of an app I am working on displays a list of active conversations with other users, text messenger style. Each conversation should preview the most recent message received. Since conversations can be carried out between multiple users, a conversation between you and John should be different from a conversation between you, John, and Jane.

I've set up two relevant models: Message and User. Message is related to User in the following way:

public function sent_to() {
    return $this->belongsToMany('App\User', 'message_users');
}

I am trying to return a list of unique conversations in my message controller like so:

$show = \App\Message::where('team_id', $team_id)
    ->where('user_id', Auth::user()->id)
    ->with(['sent_to' => function($query) {
        $query->distinct();
    }])
    ->with('user')
    ->orderBy('created_at', 'desc')->skip($offset)->take($limit)->get();

return $show;

The ->with(['sent_to'... section is where I'm a bit lost. Of course, the intent is that I get a list of unique addressees; however, I get all results. Any help is appreciated!

Update using jedrzej.kurylo's suggestion

jedrzej.kurylo suggested the following:

$show = \App\Message::where('team_id', $team_id)
    ->where('user_id', Auth::user()->id)
    ->with(['sent_to', function($query){
        $query->groupBy('id');
    }])
    ->with('user')
    ->orderBy('created_at', 'desc')->skip($offset)->take($limit)
    ->get();

return $show;

This yields the following error:

Error report

Since this is a many-to-many relationship (User linked to Message via 'message_user' table), 'id' actually refers to the id of the pivot table. What I would actually like is to get the pivot table's 'user_id'. Changing to ->groupBy('user_id') (a value on the pivot table) yields the following error: "mb_strpos() expects parameter 1 to be string, object given." Which is a radically different error.

I'm working on a work-around, and will update with it when I get it working--but it will require a couple more explicit queries. I feel like this is should be possible!

Jacob Runge
  • 392
  • 3
  • 13

2 Answers2

1

Try grouping related results by their IDs:

->with(['sent_to' => function($query) {
  $query->groupBy('id');
}])
jedrzej.kurylo
  • 39,591
  • 9
  • 98
  • 107
  • Thanks! I attempted this previously, but got an error. I think this is probably essentially what I need to do, though! I'll update my original question with details about what using groupBy() in the constraint yields. – Jacob Runge Jul 31 '18 at 01:35
  • This works--thank you! Mark Waller got me over the error that prohibited me using groupBy in the first place, however, so I am accepting his answer. – Jacob Runge Jul 31 '18 at 04:31
1

Key is in your error message "not compatible with sql_mode=only_full_group_by"

If you disable this mode your group by should work as expected.

See this question for 2 ways to disable this mode Disable ONLY_FULL_GROUP_BY

And mysql docs for more info on the setting. https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sqlmode_only_full_group_by

Mark Walker
  • 1,199
  • 11
  • 20
  • Mark, this helped a ton! I still have some work to do ironing out the kinks in the query return value, but your answer led me to a solution. A note for anyone with this issue in the future: I kept getting the error even after following the instructions above. Laravel puts mysql in 'strict' mode by default, and that needs to be turned off. See [this answer](https://stackoverflow.com/questions/43776758/how-can-i-solve-incompatible-with-sql-mode-only-full-group-by-in-laravel-eloquen) for details. – Jacob Runge Jul 31 '18 at 04:32