如何在 Laravel 中查询具有精确指定关系的数据集?

Posted

技术标签:

【中文标题】如何在 Laravel 中查询具有精确指定关系的数据集?【英文标题】:How to query dataset with exactly specified relations in Laravel? 【发布时间】:2022-01-04 09:39:23 【问题描述】:

我有以下情况:

一个表 product_mappings 和一个表 product_mapping_options。我现在想查找所有product_mappings 中的所有条目,其中分配了具有特定ID 的条目product_mapping_options。例如,这可能如下所示:

      "data" => array:4 [
        0 => array:6 [
          "id" => 4
          "product_id" => 1
          "article_number" => null
          "created_at" => "2021-11-26T09:47:25.000000Z"
          "updated_at" => "2021-11-26T09:47:25.000000Z"
          "product_mapping_options" => array:3 [
            0 => array:5 [
              "id" => 4
              "product_mapping_id" => 4
              "product_option_id" => 1
              "created_at" => "2021-11-26T09:47:25.000000Z"
              "updated_at" => "2021-11-26T09:47:25.000000Z"
            ]
            1 => array:5 [
              "id" => 5
              "product_mapping_id" => 4
              "product_option_id" => 3
              "created_at" => "2021-11-26T09:47:25.000000Z"
              "updated_at" => "2021-11-26T09:47:25.000000Z"
            ]
            2 => array:5 [
              "id" => 6
              "product_mapping_id" => 4
              "product_option_id" => 5
              "created_at" => "2021-11-26T09:47:25.000000Z"
              "updated_at" => "2021-11-26T09:47:25.000000Z"
            ]
          ]
        ]
        1 => array:6 [
          "id" => 5
          "product_id" => 1
          "article_number" => null
          "created_at" => "2021-11-26T09:47:25.000000Z"
          "updated_at" => "2021-11-26T09:47:25.000000Z"
          "product_mapping_options" => array:4 [
            0 => array:5 [
              "id" => 7
              "product_mapping_id" => 5
              "product_option_id" => 1
              "created_at" => "2021-11-26T09:47:25.000000Z"
              "updated_at" => "2021-11-26T09:47:25.000000Z"
            ]
            1 => array:5 [
              "id" => 8
              "product_mapping_id" => 5
              "product_option_id" => 3
              "created_at" => "2021-11-26T09:47:25.000000Z"
              "updated_at" => "2021-11-26T09:47:25.000000Z"
            ]
            2 => array:5 [
              "id" => 9
              "product_mapping_id" => 5
              "product_option_id" => 5
              "created_at" => "2021-11-26T09:47:25.000000Z"
              "updated_at" => "2021-11-26T09:47:25.000000Z"
            ]
            3 => array:5 [
              "id" => 10
              "product_mapping_id" => 5
              "product_option_id" => 7
              "created_at" => "2021-11-26T09:47:25.000000Z"
              "updated_at" => "2021-11-26T09:47:25.000000Z"
            ]
          ]
        ]
        2 => array:6 [
          "id" => 6
          "product_id" => 1
          "article_number" => null
          "created_at" => "2021-11-26T09:47:25.000000Z"
          "updated_at" => "2021-11-26T09:47:25.000000Z"
          "product_mapping_options" => array:4 [
            0 => array:5 [
              "id" => 11
              "product_mapping_id" => 6
              "product_option_id" => 1
              "created_at" => "2021-11-26T09:47:25.000000Z"
              "updated_at" => "2021-11-26T09:47:25.000000Z"
            ]
            1 => array:5 [
              "id" => 12
              "product_mapping_id" => 6
              "product_option_id" => 3
              "created_at" => "2021-11-26T09:47:25.000000Z"
              "updated_at" => "2021-11-26T09:47:25.000000Z"
            ]
            2 => array:5 [
              "id" => 13
              "product_mapping_id" => 6
              "product_option_id" => 5
              "created_at" => "2021-11-26T09:47:25.000000Z"
              "updated_at" => "2021-11-26T09:47:25.000000Z"
            ]
            3 => array:5 [
              "id" => 14
              "product_mapping_id" => 6
              "product_option_id" => 8
              "created_at" => "2021-11-26T09:47:25.000000Z"
              "updated_at" => "2021-11-26T09:47:25.000000Z"
            ]
          ]
        ]
        3 => array:6 [
          "id" => 7
          "product_id" => 1
          "article_number" => null
          "created_at" => "2021-11-26T09:47:25.000000Z"
          "updated_at" => "2021-11-26T09:47:25.000000Z"
          "product_mapping_options" => array:4 [
            0 => array:5 [
              "id" => 15
              "product_mapping_id" => 7
              "product_option_id" => 1
              "created_at" => "2021-11-26T09:47:25.000000Z"
              "updated_at" => "2021-11-26T09:47:25.000000Z"
            ]
            1 => array:5 [
              "id" => 16
              "product_mapping_id" => 7
              "product_option_id" => 3
              "created_at" => "2021-11-26T09:47:25.000000Z"
              "updated_at" => "2021-11-26T09:47:25.000000Z"
            ]
            2 => array:5 [
              "id" => 17
              "product_mapping_id" => 7
              "product_option_id" => 5
              "created_at" => "2021-11-26T09:47:25.000000Z"
              "updated_at" => "2021-11-26T09:47:25.000000Z"
            ]
            3 => array:5 [
              "id" => 18
              "product_mapping_id" => 7
              "product_option_id" => 9
              "created_at" => "2021-11-26T09:47:25.000000Z"
              "updated_at" => "2021-11-26T09:47:25.000000Z"
            ]
          ]
        ]
      ]

这里我通过product_mappings搜索,搜索范围如下:

    public function scopeProductOption(Builder $query, ...$productOptionTypeIds): Builder
    
        foreach($productOptionTypeIds as $productOptionTypeId) 
            $query->whereHas('ProductMappingOptions', function (Builder $query) use ($productOptionTypeId) 
                $query->where('product_option_id', $productOptionTypeId);
            );
        

        return($query);
    

在示例中,我指定了值 1,3 和 5(然后将其传递到 $productOptionTypeIds)。

我现在想要什么:我想要 100% 准确地显示 product_mappings 匹配搜索,比如说我想要找到例如只有一个条目恰好映射了 1,3 和 5,而不是 1,3,5 和 7,9 或 11。因此,具有 4 个关系的条目应该消失,因为我不希望条目在某事“结束”了。

你知道我怎样才能“抓住”这个条目吗?所以在上面的例子中,我只想要来自data 的条目0。如果我指定 1、3、5 和 9,我只想要来自 data 的条目 3。

【问题讨论】:

【参考方案1】:

好的,找到了解决方案,真的很简单:

    public function scopeProductOption(Builder $query, ...$productOptionTypeIds): Builder
    
        $query->has('ProductMappingOptions', '=', count($productOptionTypeIds));
        foreach($productOptionTypeIds as $productOptionTypeId) 
            $query->whereHas('ProductMappingOptions', function (Builder $query) use ($productOptionTypeId) 
                $query->where('product_option_id', $productOptionTypeId);
            );
        

        return($query);
    

我添加了关系计数

【讨论】:

【参考方案2】:

你可以使用whereIn函数来代替循环语句

public function scopeProductOption(Builder $query, ...$productOptionTypeIds): Builder
    
        //this will parse the values from spread operator
        if ($productOptionTypeIds !== []) 
            if (count($productOptionTypeIds) !== count($productOptionTypeIds, COUNT_RECURSIVE)) 
                $productOptionTypeIds = iterator_to_array(new \RecursiveIteratorIterator(new \RecursiveArrayIterator($productOptionTypeIds)));
            

            $query->whereHas('ProductMappingOptions', function (Builder $query) use ($productOptionTypeIds) 
                $query->where('product_option_id', $productOptionTypeIds);
            );
        
    
        return ($query);
    

【讨论】:

感谢您的回答! :-) 我现在还没有和我一起测试你的解决方案是否会导致我想要的,但在我看来它更复杂,乍一看也不是那么逻辑上可以理解。我发现我的解决方案更易于阅读和理解。

以上是关于如何在 Laravel 中查询具有精确指定关系的数据集?的主要内容,如果未能解决你的问题,请参考以下文章

Laravel:如何编写关于 belongsToMany 关系的连接计数查询?

在 Laravel 中查询用户的多对多关系

具有计数关系的 Laravel 查询构建器

Laravel - 模型自引用belongsToMany与其他关系和具有多个限制的查询

groupBy 在 laravel 中具有雄辩的关系

Laravel Eloquent 在与其他表连接后从关系中选择字段