Laravel 从父类别及其所有子类别中获取所有产品

Amr*_*ero 6 php nested laravel eloquent

我正在尝试从一个类别及其所有子类别中检索产品。

这是我的categories表:

| id    | parent_id     | name          |
|----   |-----------    |-------------  |
| 1     | NULL          | Electronics   |
| 2     | 1             | Computers     |
| 3     | 2             | Accessories   |
| 4     | 3             | Keyboards     |
Run Code Online (Sandbox Code Playgroud)

这是我的产品表:

| id    | category_id   | name          |
|----   |-------------  |-----------    |
| 1     | 2             | Product 1     |
| 2     | 3             | Product 2     |
| 3     | 4             | Product 3     |
Run Code Online (Sandbox Code Playgroud)

假设我在Computers类别页面中,我想显示这张表中的产品以及所有儿童产品。

所以应该首先从获得产品ComputersAccessoriesKeyboards

这是我的类别模型:

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

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

public function products() {
    return $this->hasManyThrough(Product::class, Category::class, 'parent_id', 'category_id', 'id');
}
Run Code Online (Sandbox Code Playgroud)

产品型号:

public function categories() {
    return $this->belongsTo(Category::class, 'category_id');
}
Run Code Online (Sandbox Code Playgroud)

查询

Category::with(['products', 'childs.products'])->where('id', $category->id)->get();
Run Code Online (Sandbox Code Playgroud)

返回

{
    "id":11,
    "parent_id":4,
    "name":"Computers",
    "products":[
        {
            "id":2,
            "category_id":12,
            "title":"Product 1",
            "laravel_through_key":11
        }
    ],
    "childs":[
        {
            "id":12,
            "parent_id":11,
            "name":"Accessories",
            "products":[
                {
                    "id":1,
                    "category_id":13,
                    "user_id":1,
                    "title":"Product 2",
                    "laravel_through_key":12
                }
            ]
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

上面,它正在转义最后一个子类别Keyboards

我曾尝试使用hasManyThrough的关系,但我只得到了产品ComputersAccessories但没有达到Keyboards

所以如果我在一个类别中,我想从这个类别树中获取所有产品。即使一个子类别有子类别。

我怎样才能做到这一点?

谢谢。

更新

我在Foued MOUSSI的回答中应用了片段:

public function childrenRecursive() {
    return $this->childs()->with('childrenRecursive');
}

$categoryIds = Category::with('childrenRecursive')->where('id', $category->id)->get();
Run Code Online (Sandbox Code Playgroud)

返回

[
    {
        "id":2,
        "parent_id":1,
        "name":"Computers",
        "children_recursive":[
            {
                "id":3,
                "parent_id":2,
                "name":"Accessories",
                "children_recursive":[
                    {
                        "id":4,
                        "parent_id":3,
                        "name":"Keyboards",
                        "children_recursive":[]
                    }
                ]
            }
        ]
    }
]
Run Code Online (Sandbox Code Playgroud)

我得到了类别数组及其所有子类别,但是要从所有这些类别中获取产品,我需要从列表中提取 IDchildrenRecursive以调用类似的内容:

Product::whereIn('category_id', $categoryIds)->get();
Run Code Online (Sandbox Code Playgroud)

任何的想法?

Fou*_*SSI 6

您可以通过以下方式修复它:

建立递归关系:(请参考 Alex Harris 的回答

// recursive, loads all descendants
// App\Category
public function childrenRecursive()
{
   return $this->childs()->with('childrenRecursive');
}

$data = Category::with(['products', 'childrenRecursive', 'childrenRecursive.products'])->where('id', 2)->get()->toArray();
Run Code Online (Sandbox Code Playgroud)

#Edit:提取产品列表

在控制器内定义Flatten laravel 递归关系集合(树集合)函数

public function flatten($array)
{
        $flatArray = [];

        if (!is_array($array)) {
            $array = (array)$array;
        }

        foreach($array as $key => $value) {
            if (is_array($value) || is_object($value)) {
                $flatArray = array_merge($flatArray, $this->flatten($value));
            } else {
                $flatArray[0][$key] = $value;
            }
        }

        return $flatArray;
}
Run Code Online (Sandbox Code Playgroud)

然后为了只有产品项目

$data = Category::with(['products', 'childrenRecursive', 'childrenRecursive.products'])->where('id', 2)->get()->toArray();

$flatten = $this->flatten($data);

foreach ($flatten as $key => $fl) {
    // eliminate categories from $flatten array
    if (!array_key_exists('category_id', $fl)) {
        unset($flatten[$key]);
    }
}

$product = array_values($flatten);
Run Code Online (Sandbox Code Playgroud)