Laravel: эффективно суммировать вложенные отношения (с нетерпением загружен)

У меня есть несколько вложенных отношений hasMany в Laravel 5.6. Я ищу эффективный способ суммирования атрибута на каждом из самых внешних узлов отношений.

Например, каждый из них связан с помощью hasMany.

Country -> States -> Cities -> Streets -> Houses

Если я хочу суммировать все House->bedrooms в данном Country, каков наиболее эффективный способ?

На данный момент у меня есть загружаемый с нетерпением Country в памяти следующим образом:

$country->load('states.cities.streets.houses');

И я попытался:

$country->states->cities->streets->houses->pluck('bedrooms')->sum()

Но получил:

Свойство [cities] не существует в этом экземпляре коллекции.

Я мог бы сделать это с помощью foreach циклов по различным отношениям, но кажется, что в Laravel должен быть более эффективный кодовый способ. Что мне не хватает?

Спасибо


person lufc    schedule 05.06.2018    source источник
comment
Пробовали ли вы withCount(): laravel.com/docs/5.6/ ?   -  person Oluwatobi Samuel Omisakin    schedule 06.06.2018


Ответы (4)


Если у вас большой результат, вы не должны получать их сумму на прикладном уровне. Попробуйте использовать функцию SQL Sum.

Это должно быть вашим лучшим решением, если вы хотите сделать это красноречивым способом:

Country::with(['states.cities.streets.houses' => function ($query) {
    $query->select('bedrooms');
}])->sum('bedrooms');

А если не использовать такие запросы к БД:

DB::table('countries')
    ->join('states', '..', '..')
    ->join('cities', '..', '..')
    ->join('streets', '..', '..')
    ->join('houses', '..', '..')
    ->selectRaw('SUM(bedrooms) as sum')
    ->pluck('sum')[0]
person Sasan Farrokh    schedule 05.06.2018
comment
Как я могу адаптировать это, чтобы использовать Country, который у меня уже есть в памяти? - person lufc; 06.06.2018
comment
используйте 1_ - person Sasan Farrokh; 05.09.2018

Я создал отношение HasManyThrough с неограниченным количеством уровней: Репозиторий на GitHub

После установки вы можете использовать его следующим образом:

class Country extends Model {
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function houses() {
        return $this->hasManyDeep(House::class, [State::class, City::class, Street::class]);
    }
}

$sum = $country->houses()->sum('bedrooms');
person Jonas Staudenmeir    schedule 29.06.2018
comment
Два года спустя я использую вашу библиотеку, это здорово. Спасибо. - person lufc; 08.12.2020

Вы можете попробовать следующий код.

Country::with(['states.cities.streets.houses' => function ($query) {
    $query->select('bedrooms', '...');
}])->sum('bedrooms');
person Mahfuz Shishir    schedule 05.06.2018

В настоящее время я решаю это, используя hasManyThrough на State и City, а затем:

$country->load('cities.houses');
echo $country->cities->sum(function ($city) {
                    return $city->houses->sum('bedrooms');
                });

Но это все еще не очень хорошо. Любые лучшие ответы очень ценятся.

person lufc    schedule 05.06.2018
comment
Немного короче: $country->cities->pluck('houses')->collapse()->sum('bedrooms') - person Jonas Staudenmeir; 06.06.2018