Laravel:如何平均嵌套 hasMany 关系(hasManyThrough)

Posted

技术标签:

【中文标题】Laravel:如何平均嵌套 hasMany 关系(hasManyThrough)【英文标题】:Laravel: how to get average on nested hasMany relationships (hasManyThrough) 【发布时间】:2015-02-26 05:15:27 【问题描述】:

我有三张桌子:

products:   id|name|description|slug|category_id|...
reviews:    id|product_id|review_text|name|email|...
review_rows id|review_id|criteria|rating

评论表存储评论文本,评论作者,并有一个外部product_id键。 review_rows 表存储不同标准的评分,例如:

----------------------------------------
| id |  criteria  | rating | review_id |
----------------------------------------
|  1 |  price     | 9      | 12        |
----------------------------------------
|  2 |  service   | 8      | 12        |
----------------------------------------
|  3 |  price     | 6      | 54        |
----------------------------------------
|  4 |  service   | 10     | 54        |
----------------------------------------

评论行通过 review_id 外键链接到评论表。我已经建立了这样的模型关系:

Product   -> hasMany   -> Review
Review    -> belongsTo -> Product
Review    -> hasMany   -> ReviewRow
ReviewRow -> belongsTo -> Review

现在我想在我的类别和产品页面上显示产品的平均评分。我怎样才能做到这一点?

我需要对每条评论的所有 reviewRows 求和和平均,然后对每条评论的所有这些求和并求平均,最终得出该产品的总体评分。这是否可以通过 Eloquent 实现,还是我需要不同的解决方案或不同的数据库设计/结构?

提前致谢!

【问题讨论】:

【参考方案1】:

您需要类似 http://softonsofa.com/tweaking-eloquent-relations-how-to-get-hasmany-relation-count-efficiently/ 的东西,只需稍作调整即可满足您的需求:

public function reviewRows()

    return $this->hasManyThrough('ReviewRow', 'Review');


public function avgRating()

    return $this->reviewRows()
      ->selectRaw('avg(rating) as aggregate, product_id')
      ->groupBy('product_id');


public function getAvgRatingAttribute()

    if ( ! array_key_exists('avgRating', $this->relations)) 
       $this->load('avgRating');
    

    $relation = $this->getRelation('avgRating')->first();

    return ($relation) ? $relation->aggregate : null;

那么就这么简单:

// eager loading
$products = Product::with('avgRating')->get();
$products->first()->avgRating; // '82.200' | null

// lazy loading via dynamic property
$product = Product::first()
$product->avgRating; // '82.200' | null

【讨论】:

哇,非常巧妙的解决方案!这非常符合 Laravel 的语法和意识形态!我有一个问题。如果我运行查询,它会返回评分以及第一个 review_row 上的所有其他信息,但我只想返回 avg(rating) 值。 它只返回 aggregateproduct_id - 我刚刚编辑了答案,之前我忘记了后者。无论如何,它不应该包括除了两者之外的任何东西。【参考方案2】:

也许你可以尝试 Eloquent 关系和 php 函数 array_reduce 的一点帮助

//model/Reviews.php
public function sum() 
    return array_reduce($this->hasMany('ReviewRows')->lists('rating'), "sumItems");  


public function sumItems ($carry, $item) 
    $carry += $item;
    return $carry;

或者使用 Eloquent RAW 查询,例如:

//model/Reviews.php
public function avg() 
   $result = $this->hasMany('ReviewRows')
   ->select(DB::raw('avg(rating) average'))
   ->first();
   return $result->average;

【讨论】:

【参考方案3】:

见https://github.com/faustbrian/laravel-commentable

public function comments(): MorphMany
    
        return $this->morphMany($this->commentableModel(), 'commentable');
    

    public function avgRating()
    
        return $this->comments()->avg("rating");
    

    $products = \App\Models\Products::with(
        [
            "comments" => function ($q) 
                $q->with(["children" => function ($qch) 
                    $qch->take(2);
                
                ])->withCount("children")->where("parent_id", '=', null);
            ,]
    )->take(5)->get();

    foreach ($products as &$product) 
        $product["avgRating"] = $product->avgRating();
    

   dd($products);


【讨论】:

【参考方案4】:

使用 laravel 官方文档 here 中提到的 withAvg()

【讨论】:

【参考方案5】:

简单易行的解决方案。将此添加到产品模型中

protected $appends = ["avg_rating"];  

public function reviewRows()

    return $this->hasManyThrough('App\ReviewRow','App\Review','product_id','review_id');


public function getAvgRatingAttribute()

    return round($this->reviewRows->average('rating'),2);

【讨论】:

您好,感谢您的回答。对你们每个人都非常有帮助,可以附加一个关于你的代码如何工作的解释!

以上是关于Laravel:如何平均嵌套 hasMany 关系(hasManyThrough)的主要内容,如果未能解决你的问题,请参考以下文章

Laravel Eloquent - 如何获得嵌套关系

Laravel Eloquent - 使用过滤数据获取嵌套关系

Laravel 过滤嵌套关系

如何在 Laravel 的 hasMany 关系中使用 groupBy

带有嵌套关系的 Laravel 急切加载

Laravel Eloquent 嵌套关系枢轴与约束