Модель сортировки Laravel на основе значений отношений

#php #laravel

#php #laravel

Вопрос:

У меня есть модель под названием Business, бизнес может иметь несколько предоставляемых ими услуг. У меня есть другая модель, называемая Payments. Платеж содержит запись о том, за какую услугу люди платят. У службы может быть много платежей. Я намерен выбрать 10 лучших и 10 худших предприятий на основе полученных платежей. Приведенный ниже код работает нормально, но он очень неэффективен. Мне приходится перебирать все данные, чтобы получить нужную мне информацию. Есть более эффективный способ достижения этого?

 $businesses = Business::with(['services'])->get();
foreach($businesses as $business){
    $id = $business->id;
    $name = $business->display_name;
    $services = $business->services;
    $businessRevenue = 0;
    if(count($services)>0){
        foreach($services as $service){
            $serviceId = $service->id;
            $totalAmount = PaymentTransaction::whereHas('invoice', function($query) use ($serviceId){
                $query->where('product_code_id', $serviceId);
            })->where('amount_paid', ">", 0)->sum('amount_paid');
            $businessRevenue= $businessRevenue   $totalAmount;
        }
    }
    $businessArray = (object) array('id'=> $id, 'name'=> $name, 'revenue'=> $businessRevenue);
    
    array_push($transformedBusiness, $businessArray);
}
$topBusiness = $bottomBusiness = $transformedBusiness;
usort($bottomBusiness, function($a, $b) {return strcmp($a->revenue, $b->revenue);});
usort($topBusiness, function($a, $b) {return strcmp($b->revenue, $a->revenue);});
$topBusiness = array_slice($topBusiness, 0, 10);
$bottomBusiness = array_slice($bottomBusiness, 0, 10);
return view('report.department_performance', compact('topBusiness', 'bottomBusiness'));  

Комментарии:

1. Прежде всего используйте Business::has('services')->get() вместо Business::with('services')->get() этого способа, вам не нужно контролировать count($services)>0

Ответ №1:

Я думаю, вы могли бы использовать запрос объединения, чтобы получить 10 лучших и самых низких компаний непосредственно из базы данных вместо того, чтобы перебирать все бизнес-записи и вручную рассчитывать их доход

Для топ-10 бизнеса вы можете использовать внутренние объединения для остальных связанных таблиц

 $topBusinesses = DB::query()
      ->select('b.id', 'b.display_name',  DB::raw('sum(p.amount_paid) as revenue')
      ->from('business as b')
      ->join('service as s', 'b.id', '=', 's.business_id')
      ->join('invoice as i', 's.id', '=', 'i.product_code_id')
      ->join('payment_transaction as p', function ($join) {
                    $join->on('p.id', '=', 'i.payment_transaction')
                     ->where('p.amount_paid', '>', 0);
      })
      ->groupBy('b.id', 'b.display_name')
      ->orderByDesc('revenue')
      ->limit(10)
      ->get();
  

Для бизнеса с наименьшим количеством 10 используйте левые соединения для invoice и payment_transaction, чтобы, если в этой таблице нет записей для бизнеса, вы все равно получите эти бизнес-записи

 $lowestBusinesses = DB::query()
      ->select('b.id', 'b.display_name',  DB::raw('coalesce(sum(p.amount_paid),0) as revenue')
      ->from('business as b')
      ->join('service as s', 'b.id', '=', 's.business_id')
      ->leftJoin('invoice as i', 's.id', '=', 'i.product_code_id')
      ->leftJoin('payment_transaction as p', function ($join) {
                    $join->on('p.id', '=', 'i.payment_transaction')
                     ->where('p.amount_paid', '>', 0);
       })
      ->groupBy('b.id', 'b.display_name')
      ->orderBy('revenue')
      ->limit(10)
      ->get();
  

Я использовал функцию coalesce MySQL для отображения значения 0 в случае, если sum() возвращает null, если вы используете любую другую базу данных, вы можете использовать альтернативную функцию.

Комментарии:

1. $lowestBusinesses = DB::query() ->select(‘b.id ‘, ‘b.display_name’, DB::raw(‘объединить(сумма (p.amount_paid), 0) как доход’)) -> от(‘бизнес как b’) -> присоединиться(‘сервис как s’, ‘b.id ‘, ‘=’, ‘s.business_id’) ->leftJoin(‘счет как я’, ‘s.id ‘, ‘=’, ‘i.product_code_id’) ->leftJoin(‘payment_transaction как p’, функция ($join) { $join-> on(‘i.id ‘, ‘=’, ‘p.payment_invoice_id’) -> где(‘p.amount_paid’, ‘>’, 0); }) -> groupBy(‘b.id ‘, ‘b.display_name’) ->OrderBy(‘доход’) -> limit(10) -> get();

2. Выше возвращено 5 элементов вместо 10. В таблице счетов-фактур нет столбца, который относится к таблице платежей. Но в таблице платежей есть столбец, который относится к счету-фактуре (payment_invoice_id).

3. @mykoman Вы уверены, что у вас есть более 5 записей в базе данных, кроме того, попробуйте изменить объединение таблицы сервисов на leftJoin('service as s)