【问题标题】:Laravel: Summing an attribute in a nested relationLaravel:对嵌套关系中的属性求和
【发布时间】:2017-06-04 21:38:00
【问题描述】:

我想对嵌套集合求和。

我有以下表格:

  • 场地
  • 优惠
  • 订单

一个venue 可以有多个offers,一个offer 可以有多个orders

示例数据可能是:

场地

id        |  name
==========================
5         |  Pizza 4 Less
10        |  Poundland

优惠

id | venue_id | name
==================================
17 | 5        | Buy 1 get one free 
24 | 5        | 30% off pizza
32 | 10       | 50% off
50 | 10       | 20% off

订单

id | offer_id | bill | paid
===========================
85 | 17       | 15   | true
86 | 17       | 20   | true
87 | 17       | 90   | true
88 | 24       | 14   | true
89 | 32       | 15   | true
90 | 32       | 65   | true
91 | 50       | 24   | true
92 | 50       | 1000 | false

我想使用 Laravel Elqouent 模型来获得每个场地支付的总金额。所以对于上面的数据我想得到如下结果:

id | name          | total_paid
===============================
5  | Pizza 4 Less  | 139
10 | Poundland     | 104

请注意,总数不包括未付款的订单(即订单 92)

我目前这样做的方式如下:

$venues = Venue::with(['offers.orders' => function ($query) {
        $query->where('paid', '=', true);
    }])
    ->get();

$totals = [];
foreach ($venues as $venue) {
    $totalPaid = 0;
    foreach ($venue->offers as $offer) {
        $totalPaid += $offer->orders->sum('bill');
    }
    $totals[$venue->name] = $totalPaid;
}

如您所见,上述代码效率低下且冗长。

有没有更好的方法来做到这一点?

【问题讨论】:

    标签: laravel laravel-5 laravel-eloquent


    【解决方案1】:

    干净的版本,但效率不高

    // In your Venue Model
    public function getTotalPaidAttribute()
    {
        return $this->offers->sum('TotalPaid');
    }
    
    // In your Offer Model
    protected $appends = ['totalPaid'];
    
    public function getTotalPaidAttribute()
    {
        return $this->orders->sum('paid');
    }
    
    // use it : 
    foreach($venues as $venue){
         //do what you want with it
         $venue->totalPaid;
    }
    

    (已编辑)

    正如cmets中所说,这种方法可能更清洁,但效率不高:

    高效的方式:

    // In your Venue Model
    public function orders(){
        return $this->hasManyThrough(Order::class, Offer::class)
            ->selectRaw('sum(paid) as aggregate, venue_id')
            ->groupBy('venue_id');
    }
    
    public function totalPaid(){
        if ( ! array_key_exists('orders', $this->relations)) $this->load('orders');
        $relation = $this->getRelation('orders');
    
         return ($relation) ? $relation->aggregate : 0;
    }
    
    public function getTotalPaidAttribute()
    {
        return $this->totalPaid();
    }
    

    也许我弄乱了你的密钥,你可能必须使用关系的完整声明:

    return $this->hasManyThrough(
            Order::class, Offer::class
            'venue_id', 'offer_id', 'id'
        );
    

    但我刚刚完成了我的项目,它就像一个魅力

    【讨论】:

    • 干净整洁
    • 这可能很干净但效率不高。尝试运行几千行的基准测试,你就会明白了。
    • 是的,但如果你知道如何做这个更清洁,我会很高兴,我尝试得到完全相同的东西,但对于 avg(),这个解决方案可能效率不高,但甚至最脏我做不到:(
    • 虽然这样干净了很多,但是这似乎和我的版本一样多的查询,所以仍然非常低效。
    猜你喜欢
    • 1970-01-01
    • 2021-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-27
    • 2021-08-05
    • 1970-01-01
    • 2019-01-24
    相关资源
    最近更新 更多