Laravel Eloquent Scope实现按父分类ID获取所有子分类课程
如何在Lesson模型中通过Eloquent Scope实现按父分类ID获取子分类下的课程
我来帮你解决这个问题,其实在Laravel里用Eloquent Scope实现这个需求很简单,下面分两种场景给出方案,你可以根据实际业务需求选择:
场景1:仅获取直接子分类下的课程
如果你的业务只需要父分类的一级子分类(比如例子中的ID3、4、5)下的课程,Scope方法可以写得很简洁:
// app/Models/Lesson.php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; class Lesson extends Model { // ... 其他模型代码 /** * 根据父分类ID筛选其直接子分类下的课程 * * @param Builder $query * @param int $parentId 父分类ID * @return Builder */ public function scopeByParentCategoryId(Builder $query, int $parentId) { // 先获取该父分类的所有直接子分类ID $childCategoryIds = Category::where('parent_id', $parentId) ->pluck('id') ->toArray(); // 筛选课程的category_id在子分类ID列表中 return $query->whereIn('category_id', $childCategoryIds); } }
使用方式
在控制器或业务逻辑中直接调用这个Scope:
$parentId = 1; $lessons = Lesson::byParentCategoryId($parentId)->get();
场景2:获取所有多级子分类下的课程
如果你的分类是多级嵌套的(比如父分类1有子分类3,子分类3还有子分类6),需要递归获取所有后代分类下的课程,推荐用数据库的递归CTE查询(性能比PHP递归更好):
// app/Models/Lesson.php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Facades\DB; class Lesson extends Model { // ... 其他模型代码 /** * 根据父分类ID筛选其所有多级子分类下的课程 * * @param Builder $query * @param int $parentId 父分类ID * @return Builder */ public function scopeByParentCategoryId(Builder $query, int $parentId) { // 用CTE递归查询所有后代分类ID(排除父分类本身) $descendantCategoryIds = DB::table('categories') ->withRecursive('category_tree', function ($query) use ($parentId) { // 初始查询:父分类本身 $query->select('id', 'parent_id') ->from('categories') ->where('id', $parentId); // 递归查询:关联子分类 $query->unionAll(function ($query) { $query->select('c.id', 'c.parent_id') ->from('categories as c') ->join('category_tree as ct', 'c.parent_id', '=', 'ct.id'); }); }) ->select('id') ->from('category_tree') ->where('id', '!=', $parentId) // 排除父分类,因为课程关联的是子分类 ->pluck('id') ->toArray(); // 筛选课程的category_id在所有后代分类ID列表中 return $query->whereIn('category_id', $descendantCategoryIds); } }
使用方式和场景1完全一致:
$parentId = 1; $lessons = Lesson::byParentCategoryId($parentId)->get();
注意事项
- 确保你的
Category模型已经正确引入(如果和Lesson不在同一命名空间,需要调整use语句) - 如果父分类没有任何子分类,两种方案都会返回空集合,避免了无效查询
- 递归CTE查询需要你的数据库支持(比如MySQL 8.0+、PostgreSQL等),如果是旧版MySQL,建议改用PHP递归获取子分类ID(性能略差但兼容性好)
内容的提问来源于stack exchange,提问作者Ahmed Nagi
相关产品推荐
相关产品推荐

