Laravel Eloquent:从三个关联表过滤并获取指定数据
需求与问题描述
我的应用中有三个数据表:products、categories和subcategories,关联关系如下:
product与category为一对多(hasMany)关系category与subcategory为一对多(hasMany)关系(subcategories表包含生产日期字段)
我需要获取所有产品的名称,以及对应的分类名称、子分类名称和日期,且数据需按子分类的dateProd字段过滤,条件为('dateProd', '>=', date('Y-m-d'))。
现有模型及数据表定义
Product 模型及数据表
模型关联代码
public function categorie() { return $this->hasMany('Categorie::class'); }
数据表字段
$table->string('name');
Categorie 模型及数据表
模型关联代码
public function product(): BelongsTo { return $this->belongsTo(Product::class); } public function subcategory(): HasMany { return $this->hasMany(Subcategorie::class); }
数据表字段
$table->string('name'); $table->foreignId('porduct_id')->constrained(); // 注意:此处存在拼写错误,应为product_id
Subcategorie 模型及数据表
模型关联代码
public function category(): BelongsTo { return $this->belongsTo(Category::class); }
数据表字段
$table->string('name'); $table->date('dateProd'); $table->foreignId('category_id')->constrained();
现有视图代码
@foreach($products as $product) {{ $product->name }} @foreach($products->category as $category) <!-- 错误:应为$product->categorie,对应模型关联方法名 --> {{ $category->name }} @foreach($category->subcategory as $subcategory) {{ $subcategory->name }} {{ $subcategory->dateProd }} @endforeach @endforeach @endforeach
解决方案
1. 修正拼写与命名规范
- 先修复
Categorie数据表中的外键拼写错误,将porduct_id改为product_id:
// Categorie 迁移文件中 $table->foreignId('product_id')->constrained();
- 统一关联方法命名(符合Laravel复数惯例):
Product模型关联改为:
public function categories() { return $this->hasMany(Categorie::class); }Categorie模型关联改为:
public function subcategories(): HasMany { return $this->hasMany(Subcategorie::class); }
2. 编写过滤查询逻辑
在控制器中使用嵌套预加载+约束查询,获取符合日期条件的数据,同时避免N+1查询问题:
use Carbon\Carbon; use App\Models\Product; $today = Carbon::today()->toDateString(); $products = Product::with([ 'categories' => function ($query) use ($today) { // 预加载分类时,过滤关联的子分类 $query->with([ 'subcategories' => function ($subQuery) use ($today) { $subQuery->where('dateProd', '>=', $today); } ]); // 只保留存在符合条件子分类的分类 $query->has('subcategories', '>', 0, 'and', function ($q) use ($today) { $q->where('dateProd', '>=', $today); }); } ]) // 只保留存在符合条件分类的产品 ->has('categories.subcategories', '>', 0, 'and', function ($q) use ($today) { $q->where('dateProd', '>=', $today); }) ->get();
如果不需要严格过滤无有效关联的产品,可去掉外层has条件。
3. 修正视图循环逻辑
根据修改后的关联方法名调整视图,同时跳过无有效子分类的分类:
@foreach($products as $product) <p>产品:{{ $product->name }}</p> @foreach($product->categories as $category) @if($category->subcategories->isNotEmpty()) <p>分类:{{ $category->name }}</p> @foreach($category->subcategories as $subcategory) <p>子分类:{{ $subcategory->name }} | 生产日期:{{ $subcategory->dateProd }}</p> @endforeach @endif @endforeach @endforeach
内容的提问来源于stack exchange,提问作者Albert Ndizeye
相关产品推荐
相关产品推荐

