PHP Lighthouse GraphQL中如何过滤方法返回的模型字段
解决GraphQL查询中过滤模型计算字段的问题
这个问题很典型——因为is_special_recipe是模型方法生成的虚拟字段,不是数据库里的列,所以直接用@where指令肯定行不通:它会尝试生成SQL WHERE条件,但数据库根本没这个字段,必然报错。下面给你两种可行的解决方案,根据你的场景选就行:
方法一:内存过滤(适合无法转成SQL的复杂逻辑)
如果你的isSpecialRecipe()方法依赖外部数据、或者逻辑复杂到没法用SQL表达,那只能先获取符合其他条件的所有数据,再在内存里过滤这个计算字段。
步骤1:修改GraphQL定义
先把is_special_recipe参数上的@where指令去掉,同时指定自定义解析器来处理查询逻辑:
searchRecipe( name: String @where(operator: "LIKE") cooking_time_min: Int @where(operator: ">=") cooking_time_max: Int @where(operator: "<=") is_special_recipe: Boolean # 移除@where指令 ): [Recipe] @paginate(resolver: "App\\GraphQL\\Resolvers\\RecipeResolver@search") type Recipe { id: ID! name: String! image: String description: String cooking_time_min: Int cooking_time_max: Int is_special_recipe: Boolean @method(name: "isSpecialRecipe") }
步骤2:编写自定义解析器
创建解析器类,先执行基础查询,再用集合过滤虚拟字段:
namespace App\GraphQL\Resolvers; use App\Models\Recipe; use Nuwave\Lighthouse\Support\Contracts\GraphQLContext; class RecipeResolver { public function search($root, array $args, GraphQLContext $context) { // 构建基础查询,处理其他数据库字段的过滤条件 $query = Recipe::query(); if (isset($args['name'])) { $query->where('name', 'LIKE', "%{$args['name']}%"); } if (isset($args['cooking_time_min'])) { $query->where('cooking_time_min', '>=', $args['cooking_time_min']); } if (isset($args['cooking_time_max'])) { $query->where('cooking_time_max', '<=', $args['cooking_time_max']); } // 获取符合基础条件的所有模型实例 $recipes = $query->get(); // 如果传递了is_special_recipe参数,在内存中过滤 if (isset($args['is_special_recipe'])) { $recipes = $recipes->filter(fn($recipe) => $recipe->isSpecialRecipe() === $args['is_special_recipe'] ); } // 处理分页逻辑(和@paginate指令兼容) $perPage = $args['perPage'] ?? 15; $page = $args['page'] ?? 1; return $recipes->forPage($page, $perPage); } }
⚠️ 注意:这种方法的缺点是数据量大时会影响性能,因为要先拉取所有符合基础条件的数据,再过滤。
方法二:转成数据库查询(推荐,性能更优)
如果你的isSpecialRecipe()逻辑完全基于数据库字段,那最好把它转成SQL条件,在数据库层面完成过滤,性能会好很多。
步骤1:在模型中定义查询作用域
假设你的isSpecialRecipe()逻辑是「烹饪时间小于10分钟,或者名称包含special」,那可以在Recipe模型里写一个查询作用域:
// App\Models\Recipe.php public function scopeWhereIsSpecialRecipe($query, $value) { // 定义核心判断逻辑的SQL条件 $specialCondition = fn($q) => $q->where('cooking_time_min', '<', 10) ->orWhere('name', 'LIKE', '%special%'); // 根据传入的布尔值,决定是过滤符合条件还是不符合条件的数据 return $value ? $query->where($specialCondition) : $query->whereNot($specialCondition); }
步骤2:修改GraphQL定义
用@scope指令替代@where,让Lighthouse调用自定义的查询作用域:
searchRecipe( name: String @where(operator: "LIKE") cooking_time_min: Int @where(operator: ">=") cooking_time_max: Int @where(operator: "<=") is_special_recipe: Boolean @scope(name: "whereIsSpecialRecipe") ): [Recipe] @paginate type Recipe { id: ID! name: String! image: String description: String cooking_time_min: Int cooking_time_max: Int is_special_recipe: Boolean @method(name: "isSpecialRecipe") }
这样就能在数据库层面完成过滤,完全不需要额外的内存处理,性能拉满。
内容的提问来源于stack exchange,提问作者SwissBread
相关产品推荐
相关产品推荐

