Home > Software design >  Laravel count has many through without n 1
Laravel count has many through without n 1

Time:07-02

I have 4 models that are relevant.

Company

Location

Customer

ThirdPartyLinkClick

Company.php

public function locations() {
        return $this->hasMany('App\Location');
    }

Location.php

public function customers() {
   return $this->hasMany('App\Customer');
}

public function linkClicks() {
   return $this->hasManyThrough('App\ThirdPartyLinkClick', 'App\Customer');
}

Customer.php

public function linkClicks() {
   return $this->hasMany('App\ThirdPartyLinkClick');
}

There is no issue when acquiring the count of link clicks for all customers when on a single Location.

I can simply do: $this->linkClicks->count(); which creates a query where it does a WHERE IN (1,2,3,4, etc) query

However, on a Company page, I want to also get this count, but avoid an n 1

Right now, my model method on Company.php is

public function getTotalClicksToReviewSites() {
  $locations = $this->locations;

  $clicks = 0;
  foreach ($locations as $location) {
    $clicks  = $location->linkClicks->count();
  }

  return $clicks;
      
}

This creates duplicate queries where it checks location_id on each query. There will be a query for every location rather than checking a group of id's in a WHERE IN statement.

How can I do an eloquent query that will use a single query to gather this data? I only need the count.

CodePudding user response:

You need to use eager loading.

$companies = Company::with('locations', 'locations.linkClicks')->get();

This prevents n 1 query problem, so, to get te total of linkClicks for each company youur function will work or simply you can do.

$companies = Company::with('locations', 'locations.linkClicks')
    ->get()
    ->map(function ($company) {
        $company->linkClicksCount = $company->locations->sum(function ($location) {
            return $location->linkClicks->count();
        });
        
        return $company;
    });

The output should be something like this (a new property linkClicksCount added on each company).

[
    {"id": 1, "name": "Company 1", "linkClicksCount": 9, "locations": [], ...},
    {"id": 2, "name": "Company 2", "linkClicksCount": 3, "locations": [], ...},
    ...
]
  • Related