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

Laravel中如何基于用户过敏原约束筛选符合要求的食谱

需求背景

我正在开发一款基于用户输入约束的膳食计划生成工具,过敏原是其中一项核心约束条件。最初设计了两张关联数据表:

  • Recipes(食谱表):存储食谱基础信息
  • Recipe_Allergens(食谱过敏原关联表):存储食谱对应的过敏原信息
    二者为一对多关系,一个食谱可包含多种过敏原,通过Recipe ID关联。

示例数据表结构

Recipes表示例

idRecipe_NameType
1LasagnaDinner

Recipe_Allergens表示例

idRecipe_IDAllergen_Description
11Dairy
21Gluten
现有实现逻辑

用户输入表单中过敏原为复选框类型,其余字段为下拉选择框,因此先对过敏原参数做拼接/拆分处理:

$request->merge([
    'allergens' => implode(',', (array)$request->get('allergens'))
]);

$meal = $request->meal_type;
$allerg = $request->allergens;
$allergenarray2 = explode(",", $allerg);

关联两张表查询符合约束的食谱ID:

$recipenew = Recipe::join('recipe_allergens', 'recipe_allergens.recipe_id', '=', 'recipe.id')
    ->where('recipe.type', $meal)
    ->whereNotIn('recipe_allergens.allergen_description', $allergenarray2)
    ->get('recipe.id');

将查询到的食谱插入膳食计划表:

$x = 0;
while ($x < $days) {
    $recipeday = (new Carbon($request->date))->addDays($x);
    $recidnew = $recipenew[$x]->id;
    MealPlanDisplay::create([
        'Recipe_ID' => $recidnew,
        'Day' =>  $recipeday,
        'user_id' => $currentuserid,
    ]);
    $x = $x + 1;
}
存在的逻辑问题

当前实现的查询逻辑存在漏洞:当用户同时提交「Dairy」和「Gluten」作为过敏原时,查询结果正常;但若用户仅提交「Dairy」作为过敏原,含有Dairy的Lasagna仍会被推荐。
原因是关联查询时,Lasagna对应的Gluten过敏原记录满足whereNotIn条件,会被单独返回,最终导致含有禁用过敏原的食谱被误判为符合要求。

修复方案

核心需求是:只要食谱含有任意一种用户提交的过敏原,就需要被排除。原来的行级过滤逻辑不符合要求,需要调整为基于食谱整体的校验逻辑,推荐两种实现方式:

方案1:使用Eloquent关联查询(推荐,更易维护)

首先在Recipe模型中定义和过敏原的关联关系:

// App\Models\Recipe.php
public function allergens()
{
    return $this->hasMany(RecipeAllergen::class, 'recipe_id');
}

查询时使用whereDoesntHave方法直接排除存在禁用过敏原的食谱:

$recipenew = Recipe::where('type', $meal)
    ->whereDoesntHave('allergens', function ($query) use ($allergenarray2) {
        // 筛选出关联过敏原在用户禁用列表中的食谱,上层doesntHave会排除这类食谱
        $query->whereIn('allergen_description', $allergenarray2);
    })
    ->get('id');

方案2:使用JOIN+分组聚合实现

如果需要直接用JOIN写法,可以按食谱ID分组,通过聚合函数判断该食谱是否含有禁用过敏原:

$recipenew = Recipe::join('recipe_allergens', 'recipe_allergens.recipe_id', '=', 'recipe.id')
    ->where('recipe.type', $meal)
    ->groupBy('recipe.id')
    // 统计该食谱下属于用户禁用过敏原的数量,数量为0才符合要求
    ->havingRaw('SUM(CASE WHEN recipe_allergens.allergen_description IN (?) THEN 1 ELSE 0 END) = 0', [implode(',', $allergenarray2)])
    ->get('recipe.id');

额外优化建议

现有写入逻辑未判断查询到的食谱数量是否满足需求天数,当符合要求的食谱数量小于$days时会出现数组越界错误,建议写入前先校验count($recipenew) >= $days,或增加随机取、重复利用符合要求的食谱的逻辑避免报错。

内容的提问来源于stack exchange,提问作者cleocoder

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.11 10:30:05