Laravel получить все товары из родительской категории и все ее подкатегории - PullRequest
0 голосов
/ 18 января 2020

Я пытаюсь получить товары из категории и всех ее подкатегорий.

Вот моя таблица categories:

| id    | parent_id     | name          |
|----   |-----------    |-------------  |
| 1     | NULL          | Electronics   |
| 2     | 1             | Computers     |
| 3     | 2             | Accessories   |
| 4     | 3             | Keyboards     |

, а вот таблица моих продуктов:

| id    | category_id   | name          |
|----   |-------------  |-----------    |
| 1     | 2             | Product 1     |
| 2     | 3             | Product 2     |
| 3     | 4             | Product 3     |

Допустим, я нахожусь на странице категории Computers, и я хочу отобразить товары из этой таблицы и все ее дочерние элементы.

, поэтому он должен сначала получить товары из Computers и Accessories а также Keyboards.

Вот моя Категория Модель:

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');
}

Продукт Модель:

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

Запрос :

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

Возврат :

{
    "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
                }
            ]
        }
    ]
}

Выше, он выходит из последней дочерней категории Keyboards.

Я пытался использовать отношения hasManyThrough, но я получил продукты только от Computers и Accessories, но не достиг Keyboards.

Так что, если я нахожусь на Категория Я хочу получить все продукты из этого дерева категорий. даже если у подкатегории есть подкатегории.

Как мне этого добиться?

Спасибо.

Обновление :

Я применил фрагмент в ответе Foued MOUSSI :

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

$categoryIds = Category::with('childrenRecursive')->where('id', $category->id)->get();

Return :

[
    {
        "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":[]
                    }
                ]
            }
        ]
    }
]

и я получил массив категория и все ее подкатегории, но чтобы получить продукты из всех этих категорий, мне нужно извлечь идентификаторы из списка с помощью childrenRecursive, чтобы вызвать что-то вроде:

Product::whereIn('category_id', $categoryIds)->get();

Есть идеи?

Ответы [ 2 ]

1 голос
/ 18 января 2020

Один из вариантов будет использовать что-то вроде laravel -adjacency-list . Это позволит вам использовать CTE для рекурсивной загрузки отношений.

Ниже приведены инструкции по настройке (на момент написания)

  1. Выполните composer require staudenmeir/laravel-adjacency-list:"^1.0"
  2. Добавьте черту HasRecursiveRelationships к вашей Category модели:

    use Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships;
    
    class Category extends Model
    {
        use HasRecursiveRelationships;
    
        ...
    }
    
  3. Измените свой запрос на:

    Category::with('descendants.products')->where('id', $id)->first(); //$id being the id of the parent category you want to get.
    

Если вы хотите просто получить продукты, которые находятся в / в категории, вы можете сделать что-то вроде:

Product::whereHas('category', function ($query) use ($category) {
    $query->whereIn('categories.id', $category->descendantsAndSelf()->select('id')->getQuery());
})->get();

Для получения дополнительной информации о том, как использовать laravel-adjacency-list, пожалуйста, обратитесь к документации .

1 голос
/ 18 января 2020

Вы можете исправить это следующим образом:

сделать рекурсивное отношение: (Пожалуйста, обратитесь к ответу Алекса Харриса здесь )

// 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();

# Редактировать: Извлечение списка продуктов

Определение Сглаживание 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;
}

Тогда для того, чтобы иметь только товарная позиция

$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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...