Home > Software design >  Is there a way to append a count of objects in a nested collection as an attribute in laravel?
Is there a way to append a count of objects in a nested collection as an attribute in laravel?

Time:01-20

I'm new to Laravel, and specially eloquent. I have a Match model and this match has goals

class Match extends Model
{
    use HasFactory;

    public function goals()
    {
        return $this->hasMany(Goal::class, 'match');
    }

}

And I'm calling it from Controller like this:

public function matchesWithGoals()
    {
        $matches = Match::with('goals')->get();
        return compact('matches');
    }

This is returning each match with a collection of its own goals. All good so far.

{
"matches": [
{
"id": 13,
"date": "2022-01-05",
"goals": [
   {
      "id": 2,
      "player": 4,
      "match": 13,
      "team": "b"
   },
   {
      "id": 3,
      "player": 13,
      "match": 13,
      "team": "b"
   },
   {
      "id": 4,
      "player": 4,
      "match": 13,
      "team": "a"
   }]
}]}

What I want to do, is take each nested collection of goals, add up based on the team, and append custom attributes "goalsTeamA", "goalsTeamB", "matchResult".

I tried to do this separate like, get all matches, get all goals, and join them. But it does seems like there should be a straight forward way to count elements inside the nested collection based on an attribute, in this case team, and append attributes so the intended result would be something like this:

{
"matches": [
   {
      "id": 13,
      "date": "2022-01-05"
      "goalsTeamA": 1,
      "goalsTeamB": 2,
      "Result": "b"
   }, 
   {
      "id": 14,
      "date": "2022-01-12"
      "goalsTeamA": 3,
      "goalsTeamB": 7,
      "Result": "b"
   }]
}

How can I achieve this?

BTW, the reason I'm trying to do it this way instead of just adding this columns to the DB with the scores is because I'm not just only be displaying scores counting up. Matches have other relationships like roster, and my goal is to learn how to process those nested relationship collections and append those results as attributes. Summing up the golas seemed like the most basic scenario.

CodePudding user response:

There are a few ways to do this, so I will provide one possible solution.

You can look into Laravel Accessors - https://laravel.com/docs/8.x/eloquent-mutators#defining-an-accessor

In your case, you could have functions on your Match model like so:

public function getGoalsTeamAAttribute() {
    // calculate and return goal count for team A
}

public function getGoalsTeamBAttribute() {
    // calculate and return goal count for team B
}

public function getResultAttribute() {
    // calculate and return result
}

You can then do one of two things to include this data on the collection.

  1. Add $appends = ['result', 'goals_team_a', 'goals_team_b']; to the top of your Match model. This will append these values onto the collection when you cast it to an array or to JSON. Google 'laravel appends' for more info.

  2. When you have a Match as an eloquent object, simply call $match->goals_team_a and it will do the calculation on the fly. You would want to be careful of n 1 problems with this approach, since it will run any database queries that are in your getGoalsTeamAAttribute function. You would want to make sure you're lazy loading (->with('goals')) before running $match->goals_team_a.

  •  Tags:  
  • Related