You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:34:58