Laravel:排除多对多关系的过滤

时间:2017-11-06 22:23:51

标签: php mysql sql laravel eloquent

我有两种型号:食品和设备。 这两个模型由多对多关系相关(也处理了我的Laravel Eloquent)。所以食物有一些必要的设备来准备它。

我想过滤含有所有输入设备的食物,并且没有任何其他设备需要这种食物。换句话说 - 当输入设备包含与某些食品相关的设备时。

提出示例的想法

例如,我有一个名为" Xyz cake"的样品。准备这份食物你需要

  • 搅拌器
  • 烘箱

让我们假设我们有以下设备的输入:

  • 搅拌器
  • 厨房机器人
  • 烘箱

在这种情况下,过滤脚本应该将我们的食物带入结果,因为与食物相关的设备适合输入设备。

另一个例子:" Abc cake"。所需设备:

  • 搅拌器
  • 厨房机器人
  • 烘箱

输入设备:

  • 搅拌器
  • 厨房机器人

在这种情况下,脚本不应该将食物带入结果,因为准备食物还需要烤箱,但输入设备中不存在烤箱。

我当前的代码

$ devices 变量是包含输入设备ID的数组。

$query = Food::query();
$query->join('equipments_foods', function ($join) use ($equipments) {
    $join->on('foods.id', '=', 'equipments_foods.food_id');
    $join->whereIn('equipments_foods.equipment_id', $equipments);
});
$results = $query->get();

此代码无法正常运行,因为它提供的Foods包含至少一台来自输入设备的设备。

数据库是MySQL。

请帮我实现正确的过滤效果。

非常感谢!

3 个答案:

答案 0 :(得分:1)

我认为以下内容适用于您的问题。这是一个要测试的SQL小提琴。 http://sqlfiddle.com/#!9/d04555/3

基本上,它会解决问题,并使用未经选择的设备查找和排除所有食物。

SELECT f.*
FROM food f
WHERE NOT EXISTS (
    SELECT *
    FROM equipment e 
    INNER JOIN equipments_foods ef ON (e.id = ef.equipment_id)
    WHERE e.id NOT IN (1, 2, 3)
    AND f.id = ef.food_id
)

然后您可以创建一个模型查询范围来包含此逻辑。以下是使用Laravel的查询构建器进行未经测试的尝试。

$foods = Food::usingEquipment($equipment)->get();

public function scopeUsingEquipment(Builder $query, array $equipment)
{
    return $query->whereNotExists(function ($query) use ($equipment) {
        $query->select(DB::raw(1))
            ->from('equipment')
            ->join('equipments_foods', 'equipment.id', '=', 'equipments_foods.equipment_id')
            ->whereIn('equipment.id', $equipment)
            ->whereRaw('food.id = equipments_foods.food_id');
    });
}

答案 1 :(得分:1)

我会建议你使用laravel的多对子数据表。 你的食物模型应该是这样的

命名空间App;

使用Illuminate \ Database \ Eloquent \ Model;

class Food扩展Model {     protected $ fillable = [' name'];

公共功能设备(){

返回$ this> belongsToMany(' App \ equipment',' food_equipment',' food_id',' equipment_id');

}

public function scopeRel($ query){

使用('设备')返回$ query-&gt ;; }

}

food_equipment是数据透视表,而food_id和equipment_id是数据透视表中的字段。 这种关系确保了制作不同种类食物所需的所有设备都得到了适当的匹配。

scopeRel是一个本地范围,它将返回准备食物所需的所有设备。

您可以使用查询模型。 食物::其中(' equipment_id',1) - > rel() - > first();

答案 2 :(得分:0)

在研究找到正确的解决方案之后,我终于使用Laravel Query Builder来实现它。

我并不认为这很容易。

$query = Food::query();
$query->where(function ($query) use ($inputEquipment) {
    $query->doesntHave('equipments');
    $query->orWhere(function ($query) use ($inputEquipment) {
        foreach ($inputEquipment as $equipmentId) {
            $query->whereHas('equipments', function ($query) use ($equipmentId) {
                $query->where('id', $equipmentId);
            });
        }
    });
});
$results = $query->get();