Php 如何使用Laravel中的多对多关系检索嵌套类别的产品并对结果排序?
我想检索所选类别以及子类别的产品,甚至可能需要根据价格或日期进行订购。请让我知道应该对控制器进行哪些更改 我对两个表使用了多对多关系:Categories和Products,以及category_product来关联这两个表 示例Php 如何使用Laravel中的多对多关系检索嵌套类别的产品并对结果排序?,php,laravel,categories,voyager,Php,Laravel,Categories,Voyager,我想检索所选类别以及子类别的产品,甚至可能需要根据价格或日期进行订购。请让我知道应该对控制器进行哪些更改 我对两个表使用了多对多关系:Categories和Products,以及category_product来关联这两个表 示例 Products: id, name, price, created_at (并非所有类别都有子类别) 礼物 书 玩具 男孩 姑娘们 电话 三星 诺基亚 如果用户单击手机,则应显示“手机”、“三星”或“诺基亚”类别的所有产品 数据库 Products: id
Products: id, name, price, created_at
(并非所有类别都有子类别)
礼物
书
玩具
- 男孩
- 姑娘们
- 三星
- 诺基亚
Products: id, name, price, created_at
代码:
类别模型:
class Category extends Model
{
public function products()
{
return $this->belongsToMany('App\Product');
}
public function parent()
{
return $this->belongsTo('App\Category', 'parent_id');
}
public function children()
{
return $this->hasMany('App\Category', 'parent_id');
}
}
public function nestedStructure()
{
return $this->children()->with([
'nestedStructure',
'products' => function($q) {
$q->orderBy('created_at', 'desc');
}
);
}
产品模型
class Product extends Model
{
public function categories()
{
return $this->belongsToMany('App\Category');
}
}
看法
{{csrf_field()}}
排序方式:
最新的
价格由低到高
价格由高到低
@foreach($category->products as$product)
image)}}“alt=“{{$product->name}}”>
{{$product->name}
${{$product->price}
@endforeach
我正在使用Laravel 6.2版和Voyager 1.3版。如果您的类别深度不受限制,则需要递归关系 我认为这样的方法可以奏效: 在您的类别模型中:
class Category extends Model
{
public function products()
{
return $this->belongsToMany('App\Product');
}
public function parent()
{
return $this->belongsTo('App\Category', 'parent_id');
}
public function children()
{
return $this->hasMany('App\Category', 'parent_id');
}
}
public function nestedStructure()
{
return $this->children()->with([
'nestedStructure',
'products' => function($q) {
$q->orderBy('created_at', 'desc');
}
);
}
好的,所以首先你需要修改你的分类模型,让所有的孩子和父母在一起 类别模型
class Category extends Model
{
public function products()
{
return $this->belongsToMany('App\Product');
}
public function parent()
{
return $this->belongsTo('App\Category', 'parent_id');
}
public function children()
{
return $this->hasMany('App\Category', 'parent_id');
}
// recursive, loads all descendants
public function recursiveChildren()
{
return $this->children()->with('recursiveChildren');
}
}
现在,根据给定的多对多关系,一个产品可能属于多个类别,但我们不希望同一个产品一再出现。因此,您的控制器可能的修复方法是
class ProductController extends Controller {
public function index($slug, Request $request)
{
$categories = Category::where('slug', $slug)->with('recursiveChildren')->whereNull('parent')->get();
if($categories->count() == 0) abort(404, 'Page not found');
$products = collect([]); // its a helper method to make collections
foreach($categories as $category) {
$category_products = $category->products;
foreach($category_products as $product) {
if(!$products->contains($product)) $products->push($product);
}
}
if($request->input('soryBy')) {
switch ($request->sortBy)
{
case 'latest':
$products = $products->sortBy('created_at');
break;
case 'asc':
$products = $products->sortBy('price');
break;
case 'desc':
$products = $products->sortByDesc('price');
break;
default:
$products;
break;
}
}
return view('products', compact('products'));
}
}
现在让我们稍微修改一下视图
<form id="sortProducts" class="form-inline" method="get">
{{ csrf_field() }}
<label for="sortBy">Sort By:</label>
<select name="sortBy" id="sortBy">
<option value="latest">Latest</option>
<option value="asc">Price Low to Hight</option>
<option value="desc">Price High to Low</option>
</select>
</form>
@foreach($products as $product)
<div class="product">
<img border="0" src="{{Voyager::image($product->image)}}" alt="{{$product->name}}">
<div class="product-name">{{$product->name}}</div>
<div class="product-price">${{$product->price}}</div>
</div>
@endforeach
{{csrf_field()}}
排序方式:
最新的
价格由低到高
价格由高到低
@foreach($products as$product)
image)}}“alt=“{{$product->name}}”>
{{$product->name}
${{$product->price}
@endforeach
至少有两种解决方案
溶液1(纯Laravel):
将这两种方法添加到您的类别中
型号:
public function descendants()
{
$collection = new \Illuminate\Support\Collection();
foreach ($this->children as $child) {
$collection->add($child);
$collection = $collection->merge($child->descendants());
}
return $collection;
}
public function getRouteKeyName()
{
return 'slug';
}
并在您的产品控制器中使用它,如下所示:
class ProductController extends Controller
{
public function index(Request $request, Category $category)
{
$categories = $category->descendants()->add($category)->pluck('id');
$products = DB::table('category_product AS cp')
->join('products', 'cp.product_id', '=', 'products.id')
->select('products.*')
->whereIn('cp.category_id', $categories)
->get();
return view('products', compact('category', 'products'));
}
}
然后可以在视图文件中输出它们:
@forelse($products as $product)
<div class="product">
<img border="0" src="{{ Voyager::image($product->image) }}" alt="{{ $product->name }}">
<div class="product-name">{{ $product->name }}</div>
<div class="product-price">${{ $product->price }}</div>
</div>
@empty
<div class="product">
There are no products in this category.
</div>
@endforelse
用$table->nestedSet()替换parent_id
列代码>在您的类别
表和相关迁移文件中:
Schema::create('categories', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('slug')->unique();
$table->nestedSet();
$table->timestamps();
});
然后,更新您的类别
型号,如下所示:
use Illuminate\Database\Eloquent\Model;
use Kalnoy\Nestedset\NodeTrait;
class Category extends Model
{
use NodeTrait;
protected $fillable = [
'name',
'slug',
];
public function products()
{
return $this->belongsToMany('App\Product');
}
public function parent()
{
return $this->belongsTo(self::class, 'parent_id');
}
public function children()
{
return $this->hasMany(self::class, 'parent_id');
}
public function getRouteKeyName()
{
return 'slug';
}
}
现在,您可以在控制器中使用它:
class ProductController extends Controller
{
public function index(Request $request, Category $category)
{
$categories = Category::descendantsAndSelf($category->id)->pluck('id');
$products = DB::table('category_product AS cp')
->join('products', 'cp.product_id', '=', 'products.id')
->select('products.*')
->whereIn('cp.category_id', $categories)
->get();
return view('products', compact('category', 'products'));
}
}
您可以按解决方案1所示进行输出
请注意,我假设您在路线定义中使用{category}
键。(见)例如:
Route::get('/products/category/{category}', 'ProductController@index');
阅读第一个以创建、更新和删除嵌套集合(类别)中的项目。如果选择第一个类别,例如$category->children->first()->products()
@Joseph,您可以检索它。您能再解释一下吗?不是所有类别都有子类!请看这篇文章:我尝试了您提供的解决方案,但不幸的是,无法检索父类别的产品!对于子类别,子类别链接显示404错误。在我注释了abort(404,“找不到页面”)
之后,即使对于子类别,也没有检索到产品。因此,我删除了whereNull('parent_id')
,解决了这个问题,但是父类别仍然没有显示产品!!!
class ProductController extends Controller
{
public function index(Request $request, Category $category)
{
$categories = Category::descendantsAndSelf($category->id)->pluck('id');
$products = DB::table('category_product AS cp')
->join('products', 'cp.product_id', '=', 'products.id')
->select('products.*')
->whereIn('cp.category_id', $categories)
->get();
return view('products', compact('category', 'products'));
}
}
Route::get('/products/category/{category}', 'ProductController@index');