2
votes

I am using Eloquent with Laravel.

The case: I'm building an API where there is possibility to include relations for a Resource. So for example /api/teams?include=users will add the User model for every Team. For the logic that includes the relationship I'm using Fractal. So I need to have some logic that determines which relationship has to be included, so I can create a optimized query for it.

Problem: When I want to render a collection of a Team with the related User models. I can eager-load the models just fine. The problems comes when I have custom attributes on the User model. These will cause a N+1 query problem because for every eager-loaded team, because the query for the custom attributes will be executed for every model.

Example code:

// The Team model with the custom attribute
class Team extends Model {
    protected $appends = ['is_member'];

    public function getIsMemberAttribute() {
        $loggedUser = Auth::currentUser();

        $result = DB::table('team_user')
                        ->where('team_id', $this-id)
                        ->where('user_id', $loggedUser->id)
                        ->get();

        return !is_null($result);
    }
}

// The controller code
$team = Team::findOrFail($teamId);

// So this will return all the User models that belong to the Team.
// The problem is this will execute the query inside the getIsMemberAttribute() for every User model.
dd($team->users);

Is there a good pattern to solve this issue?

1
Provide some code. That would help - Jilson Thomas
@JilsonThomas added some code ;)! - guidsen

1 Answers

0
votes

You could iterate through the User models and see if one of them matches the logged in user. It's more efficient than looking it up in the database.

class Team extends Model {
    protected $appends = ['is_member'];

    public function getIsMemberAttribute() {
        $loggedUser = Auth::currentUser();

        foreach ($this->users as $user) {

            if ($user->id == $loggedUser->id) {
                return true;
            }
        }

        return false;
    }
}