我正在尝试从一个类别及其所有子类别中检索产品。
这是我的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();
返回:
[
{
"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
从列表中提取ID来调用类似的内容:
Product::whereIn('category_id', $categoryIds)->get();
有什么主意吗?
答案 0 :(得分:2)
您可以通过以下方式修复它:
建立递归关系:(请参阅Alex Harris的答案here)
// 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();
#Edit:提取产品列表
在控制器内定义Flatten laravel recursive relationship collection (tree collections)函数
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);
答案 1 :(得分:2)
一种选择是使用类似laravel-adjacency-list的名称。这将允许您使用CTE来递归加载关系。
以下是进行设置的步骤(在撰写本文时)
composer require staudenmeir/laravel-adjacency-list:"^1.0"
将HasRecursiveRelationships
特性添加到您的Category
模型中:
use Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships;
class Category extends Model
{
use HasRecursiveRelationships;
...
}
将查询更改为:
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
的更多信息,请参阅documentation。
答案 2 :(得分:1)
由于我使用的是 MySQL 5,如果有人对此感兴趣,我会在我的项目中使用这种方式来解决此问题。这对我来说更有意义。
如果您使用的是 MySQL 8,您可以将此问题作为参考来编辑此代码。 How to create a MySQL hierarchical recursive query?
public function scopeWhereCategoryId($query, $category_id){
return $query->where(function($query) use ($category_id){
$query->where('category_id', trim($category_id));
$query->orWhereRaw("category_id in (
select id
from (select * from pm_categories
order by category_id, id) categories_sorted,
(select @pv := '" . trim($category_id) . "') initialisation
where find_in_set(category_id, @pv)
and length(@pv := concat(@pv, ',', id))
)");
});
}
答案 3 :(得分:1)
如果你使用 ORM Eloquent 关系
public function categoriesProduct(ProductCategory $category)
{
$categories = ProductCategory::where('parent_id', $category->id)
->orWhere('id', $category->id)
->latest()
->get();
return view('categoryProduct', compact('categories', 'category'));
}
答案 4 :(得分:0)
$categories = Category::whereParentId(null)->with('children.children.children')->get();
在视图中,您可以显示带有foreach循环的项目