如何使用 Laravel 查询构建器构建这样的查询?

问题描述 投票:0回答:2

有一个MySQL“类别”表。

类别模型具有如下所示的“hasMany”关系。

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

在类别编辑页面上,为了将父类别分配给当前类别(为此我需要填写其parent_id字段),我必须生成一个选择标签,该标签将包含类别表中的所有行,除了当前类别及其所有子类别。

category_id  | parent_id
------------------------
1            | NULL
2            | 1
3            | 2
4            | 3
5            | NULL
6            | 5

For example,
for category_id 1, should be selected lines with category_id [5, 6]
for category_id 2, should be selected lines with category_id [1, 5, 6]
for category_id 3, should be selected lines with category_id [1, 2, 5, 6]
for category_id 4, should be selected lines with category_id [1, 2, 3, 5, 6]
for category_id 5, should be selected lines with category_id [1, 2, 3, 4]
for category_id 6, should be selected lines with category_id [1, 2, 3, 4, 5]

如果某个类别的parent_id为NULL,则表示该类别没有父类别。

php mysql laravel hierarchical-data laravel-query-builder
2个回答
0
投票

简短回答:您需要选择当前类别中的所有记录

EXCEPT
递归记录。

对于递归部分,您可以使用以下方法:https://github.com/staudenmeir/laravel-adjacency-list

查看它默认包含的

descendantsAndSelf()
关系。

接下来您需要构建

EXCEPT
查询。查询构建器不包含 except 函数,因此您需要自己构建一个。

例如,我的一个控制器中有这个:

我有一个名为

Thread
的雄辩模型,这是一个用于保存消息线程的模型。人们对这些线程有不同的查看权限,因此我需要多个查询来过滤掉他们可以看到和不能看到的线程。

我使用元素查询生成器预先构建这些单独的查询,有些我

UNION
。比有一个查询明确排除线程。然后我将联合查询和
$excludeExplicit
查询结合起来:

$query = Thread::query() 
    ->fromRaw( 
        '(SELECT * FROM ((' . $unioned->toSql() . ') EXCEPT ' . $excludeExplicit->toSql() . ') AS threads) AS threads', 
        array_merge($unioned->getBindings(), $excludeExplicit->getBindings()) 
    );

(参见https://stackoverflow.com/a/64231210/9258054

这个原始查询的技巧在于参数绑定的合并。此外,此查询的结果是 Eloquent 模型。请注意,当使用

UNION
EXCEPT
等查询时,您需要确保两者的
SELECT
语句返回相同的列。


0
投票

在深入研究代码之后,我找到了一个更优雅的解决方案。 我正在使用“文章”模型中创建的“类别”关系从数据库中进行选择

public function categories(){
    return $this->morphToMany(Category::class, 'categoryable');
}

这样做时,我只选择那些没有父类别的项目。 但是由于“类别”关系,所有子项都转到“文章”集合,并且项目树已正确构建,并且我不必使用“加入”进行复杂的查询。

public function topMenu(){
    View::composer('layouts.header', function ($view){
        $view->with('categories', Category::whereNull('parent_id')->where('published', 1)->get());
    });
}
© www.soinside.com 2019 - 2024. All rights reserved.