Home > Software engineering >  How to query dataset with exactly specified relations in Laravel?
How to query dataset with exactly specified relations in Laravel?

Time:11-27

I have the following situation:

A table product_mappings and a table product_mapping_options. I now want to find all entries within all product_mappings to which an entry product_mapping_options with certain ids is assigned. For example, this could look like this:

      "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"
            ]
          ]
        ]
      ]

Here I searched via product_mappings with the following search scope:

    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);
    }

In the example I specified the values 1,3 and 5 (which was then passed into $productOptionTypeIds).

What I would like now: I would like to get 100% exactly displayed which product_mappings match the search, Say I would like to find e.g. only one entry where exactly 1,3 and 5 is mapped and not also 1,3,5 and 7,9 or 11. So the entries which then have 4 relations should fall away, because I don't want to have the entries where something is "over".

Do you have any idea how I can "catch" exactly this one entry? So in the example above I would only want entry 0 from data. If I specify 1,3,5 and 9, I would only want entry 3 from data.

CodePudding user response:

Okay, found a solution and it was really easy:

    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);
    }

I've added a counting for relations

CodePudding user response:

Instead of loop statment you can use whereIn function

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);
    }
  • Related