1

I have an interface which displays a list of communities on the platform. Communities have members and in turn members/profiles can befriend one another. On the listing page each community card needs to display the number of members (in the community) and the number of friends (friends of logged in profile) from those members.

Here's an illustration of how a community card looks like

enter image description here

I'm getting the communities with the members first:

$communities = $loggedInProfile->communities->load('members')->take(15);

And then iterating over the communities and then the members to find out which ones are friends with the logged in user.

   foreach ($communities as $key => $community) {
        $friends = [];
        foreach ($community->members as $member) {
            if ($loggedInProfile->isFriendWith($member)) {
                array_push($friends, $member);
            }
        }
        $community->members_who_are_friends = $friends;
    }

My issue is that this is very taxing in terms of the number of queries when the associations get large. Is there a better way of retrieving these relationships without having to use nested for loops? I'm also indexing all data with Elasticsearch. Would a retrieval of this sort be better with Elasticsearch? Also would this be a good use case for hasThrough?

Update

The members relationship:

public function members()
{
    return $this->belongsToMany('App\Profile', 'community_members', 'community_id', 'profile_id')->withTimestamps();
}

The isFriendWith relationship:

public function isFriendWith(Model $recipient)
{
    return $this->findFriendship($recipient)->where('status', Status::ACCEPTED)->exists();
}

The check is done on a table called friendships. The status column (which can be either 0 or 1) is checked to see if friends or not.

The findFriendship check:

private function findFriendship(Model $recipient)
{
    return Friendship::betweenModels($this, $recipient);
}

Database structure:

-Profiles migration

Schema::create('profiles', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('user_id');
            $table->foreign('user_id')->references('id')->on('users');
});

-Communities migration (the foreign key is the owner of the community)

Schema::create('communities', function (Blueprint $table) {
    $table->increments('id');
    $table->unsignedInteger('profile_id');
    $table->foreign('profile_id')->references('id')->on('profiles');
    $table->string('slug')->unique();
});

-Community_members migration

Schema::create('community_members', function (Blueprint $table) {
    $table->primary(['profile_id', 'community_id']);
    $table->unsignedInteger('profile_id');
    $table->foreign('profile_id')->references('id')->on('profiles');
    $table->unsignedInteger('community_id');
    $table->foreign('community_id')->references('id')->on('communities');
    $table->timestamps();
});

-Friendships migration

Schema::create('friendships'), function (Blueprint $table) {
    $table->increments('id');
    $table->morphs('sender');
    $table->morphs('recipient');
    $table->tinyInteger('status')->default(0);
    $table->timestamps();
});

1 Answers1

0

In your line:

$communities = $loggedInProfile->communities->load('members')->take(15);

load() is used to perform Lazy Eager loading, i.e. you load the members after the communities have been retrieved, resulting in a different query for every community. You could extract the whole data with a single query using with(). Also, take(15) is performed on the resulting collection and not on the query. Try this:

$communities = $loggedInProfile->communities()->with('members')->take(15)->get();
gbalduzzi
  • 9,356
  • 28
  • 58