【问题标题】:Get all pages under a root category ID in belongsToMany relationship获取belongsToMany关系中某个根类别ID下的所有页面
【发布时间】:2019-12-03 09:42:07
【问题描述】:

这是我过去几个小时试图解决的一个挑战。

我有一个Page 模型和一个Category 模型。

一个页面可以属于多个类别,每个类别可以有多个页面,因此它们之间具有多对多的关系。

最重要的是,每个类别都有一个父类别,因此每个类别之间是一对多的。

因此,考虑到所有这些,以下是模型:

页面模型

// table ['id', 'title', 'content', 'created_at', ...]
class Page extends Model
{
    public function categories()
    {
        return $this->belongsToMany(Category::class);
    }
}

类别模型

// table ['id', 'category_id', 'name', 'created_at', ...]
class Category extends Model
{
    public function pages()
    {
        return $this->belongsToMany(Page::class)
    }

    public function categories()
    {
        return $this->hasMany(Category::class);
    }

    public function parent()
    {
        return $this->belongsTo(Category::class, 'category_id');
    }
}

我确实有一个category_page 表与category_idpage_id 来链接这种关系。

现在是棘手的部分。

我正在尝试创建一个 Eloquent 查询,给定 category_id 我将能够获取此类别下的所有页面以及此类别的子类别下的所有页面。

我可能可以通过嵌套 SQL 查询中的多个连接或简单的 foreach 循环来递归地获取子页面来实现这一点(但这会花费大量不必要的时间)。

有什么好的优雅的方法吗?

提前致谢。

编辑:

在 Laravel 中有一种方法可以建立递归关系,并在一行代码中包含所有带有页面的子类别。

//在类别模型中

public function childrenCategories()
{
    return $this->hasMany(Category::class)->with('childrenCategories');
}

现在我可以了

Category::whereId($rootCategoryId)->with('childrenCategories')->first();

这样我就有了一个所有类别的嵌套树,然后我可以加载它的页面关系,我就完成了。

但是,它存在一个巨大的问题,它执行的查询量非常大(每个嵌套级别的查询),并且在生产应用程序中使用它没有意义。 所以我确实有一个解决方案,这很糟糕,所以我愿意接受其他建议。

【问题讨论】:

    标签: laravel eloquent


    【解决方案1】:

    如果你使用 id,你可以使用嵌套的whereHas():

    $pages = Page
        ::whereHas('categories', function ($query) use ($categoryId) {
            $query
                ->where('categories.id', $categoryId)
                ->orWhereHas('parent', function ($query) use ($categoryId) {
                    $query->where('id', $categoryId);
                });
        })
        ->get();
    

    如果您正在使用模型,您有 2 个选项:

    1) 嵌套whereHas() 方法:

    $pages = Page
        ::whereHas('categories', function ($query) use ($category) {
            $query
                ->where('id', $category->id)
                ->orWhereHas('parent', function ($query) use ($category) {
                    $query->where('id', $category->id);
                });
        })
        ->get();
    

    2) 收集当前类别 id 和她的孩子 id,然后使用一个 whereHas() 方法。

    $categoryIds = $category->categories->pluck('id')->push($category->id);
    
    $pages = Page
        ::whereHas('categories', function ($query) use ($categoryIds) {
            $query->whereIn('categories.id', $categoryIds);
        })
        ->get();
    

    更多:https://laravel.com/docs/6.x/eloquent-relationships#querying-relationship-existence

    【讨论】:

    • 感谢您的快速回复,看起来您的查询只会让我降级(类别->页面和子类别->页面)。如果我想从当前的类别 ID 一路向下(有时可能是 3-4 级深),它就行不通了。我说的对吗?
    • 是的,你是对的。您的表结构对此无效。您应该查看嵌套集技术(laravel 中的良好实现 - github.com/lazychaser/laravel-nestedset)。它允许您在一次查询中获取所有后代类别,然后您可以通过第二次查询获取它们的页面。
    猜你喜欢
    • 2018-05-19
    • 1970-01-01
    • 2021-07-28
    • 1970-01-01
    • 1970-01-01
    • 2020-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多