【问题标题】:Laravel eloquent get relation countLaravel eloquent 获取关系计数
【发布时间】:2017-05-01 03:33:30
【问题描述】:

我使用 Laravel 5.3。

我有 2 张桌子:

Articles
---------
id
cat_id
title

Category
---------
id
parent_id
title

我已经在我的模型中定义了我的关系:

// Article model
public function category()
{
    return $this->belongsTo(Category::class);
}

// Category model
public function children() 
{
    return $this->hasMany(Category::class, 'parent_id', 'id');
}   

有没有一种简单的方法使用 Eloquent 列出包含文章数量的类别。困难在于我想对id_parent = 0 所在的类别进行分组,即我只想显示具有子项文章数的父类别。

我尝试过类似的方法:

    $category = new \App\Models\Category();
    $categoryTable = $category->getTable();

    return $category->leftJoin('article', 'article.cat_id', '=', 'category.id')
        ->whereIn('article.cat_id', function($query)
            {
                $query->select('cat_id')
                    ->from('categories')
                    ->where('categories.parent_id', ???)
                    ->orWhere($this->tableName .'.cat_id', $id);
            })
        ->groupBy('cat_id');

但我迷路了……

【问题讨论】:

  • category中有多少层次结构?
  • 最多只有 2 个,不能更多

标签: php laravel


【解决方案1】:

您可以使用withCount()。从 5.3 版本开始提供

更多关于 eloquent 的信息请访问:https://laravel.com/docs/5.3/eloquent-relationships

【讨论】:

  • 啊,太好了!它有效,但我在这里有两个关系。它适用于关系文章 -> 类别,但我也需要包括孩子。
  • 是的!谢谢!之后我们可以在 Entity ex 的属性中访问这些值:$category->articles_count
  • withCount 在我每次尝试时几乎都是 n+1 - 对于一个小型 10 行数据库来说很好,但只是将你的行增加到 1000 行,然后突然它会执行 1000 次查询来计算每个项目
【解决方案2】:

Category 模型中定义articles() 关系为:

public function articles() 
{
    return $this->hasMany(Article::class, 'cat_id');
}

那你可以试试:

Category::where('parent_id', 0)->withCount('articles')->get();

【讨论】:

  • 但是如何计算 parent with children 类别中的文章数?
  • 是的,但我也需要计算儿童类别的数量。我的意思是如果一篇文章属于儿童类别,我需要计算它。
【解决方案3】:

您可以使用hasManyThrough() Eloquent 方法获取所有儿童文章,然后将文章计数添加到一个漂亮的小getter 中。我将 getter 添加到模型上的 $appends 数组中,以帮助在 Tinker 输出中说明它。

class Category extends Model
{

    protected $appends = [
        'articleCount'
    ];

    public function articles()
    {
        return $this->hasMany(Article::class);
    }

    public function children()
    {
        return $this->hasMany(Category::class, 'parent_id');
    }

    public function childrenArticles()
    {
        return $this->hasManyThrough(Article::class, Category::class, 'parent_id');
    }

    public function getArticleCountAttribute()
    {
        return $this->articles()->count() + $this->childrenArticles()->count();
    }
}

这是 Tinker 的输出:

Psy Shell v0.8.0 (PHP 7.0.6 — cli) by Justin Hileman
>>> $cat = App\Category::first();
=> App\Category {#677
     id: "1",
     name: "Cooking",
     parent_id: null,
     created_at: "2016-12-15 18:31:57",
     updated_at: "2016-12-15 18:31:57",
   }
>>> $cat->toArray();
=> [
     "id" => 1,
     "name" => "Cooking",
     "parent_id" => null,
     "created_at" => "2016-12-15 18:31:57",
     "updated_at" => "2016-12-15 18:31:57",
     "articleCount" => 79,
   ]
>>> 

如果您想将您的类别查询限制为具有文章的子项的查询,您可以使用has() 方法来做到这一点:

Category::has('children.articles')->get();

这里有更多关于 has() 方法的信息:

https://laravel.com/docs/5.3/eloquent-relationships#querying-relationship-existence

还有hasManyThrough() 方法:

https://laravel.com/docs/5.3/eloquent-relationships#has-many-through

【讨论】:

    【解决方案4】:

    这应该可行:

    $category
    ->where('categories.parent_id', 0)
    ->leftJoin('article', 'article.cat_id', '=', 'categories.id')
    ->select('categories.id', \DB::raw('COUNT(article.id)'))
    ->groupBy('categories.id')
    ->get();
    

    上述查询将为您获取类别 ID 和属于该类别的所有文章的计数。

    再次阅读您的问题和 cmets 后,如果我理解正确,您想获取属于这些类别的所有文章的计数(parent_id = 0)+ 属于子类别的文章计数(那些具有 parent_id = (某个类别的 ID))。

    现在我无法轻松地对此进行测试,但我认为这些方面的东西应该可以解决这个问题。

    $category
    ->where('categories.parent_id', 0)
    ->leftJoin('article', 'article.cat_id', '=', 'categories.id')
    ->leftJoin('categories as c2', 'c2.parent_id', '=', 'categories.id')
    ->leftJoin('article as a2', 'a2.cat_id', '=', 'c2.id')
    ->select('categories.id', \DB::raw('(COUNT(article.id)) + (COUNT(a2.id)) as count'))
    ->groupBy('categories.id')
    ->get();
    

    也就是说,我认为您最好在类别中拥有一个名为 count 的列,并在每次添加新文章时更新它。为了性能。

    【讨论】:

      【解决方案5】:

      我确信有人还在经历这个问题,我能够通过以下方式解决它,假设我有一个 Agent 模型和一个 Schedule 模型,即一个代理可能有多个计划:

      class Schedule extends Model {
        public function agent() {
          return $this->belongsTo(Agent::class, 'agent_id');
        }
      }
      
      class Agent extends Model {
        public function user(){
          return $this->belongsTo(User::class);
        }
      
        public function schedules(){
          return $this->hasMany(Schedule::class);
        }
      }
      

      有些代理可能不一定有分配的时间表,因此,我在调用with() 方法之前过滤了这些,如下所示:

      $agents = Agents::whereIn(
          'id', 
          Schedule::distinct()->pluck('agent_id')
      )->with('schedules')->get();
      

      希望这会有所帮助!

      【讨论】:

        猜你喜欢
        • 2013-12-14
        • 2017-01-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-08
        • 2020-06-22
        相关资源
        最近更新 更多