Browse Source

Product.VariantBySelectedOptions

- implementation
Alexey Kim 2 years ago
parent
commit
f0368cb65b
5 changed files with 96 additions and 8 deletions
  1. 1 0
      db/product.go
  2. 74 0
      graphql/query_product.go
  3. 1 1
      graphql/schema.graphql
  4. 6 7
      relation/product.go
  5. 14 0
      relation/product_variant.go

+ 1 - 0
db/product.go

@@ -88,6 +88,7 @@ var (
 			"any(t.image) as image",
 			"any(t.inventory_management) as inventory_management",
 			"any(t.inventory_policy) as inventory_policy",
+			"any(t.options) as options",
 			"any(t.grams) as grams",
 			"any(t.weight) as weight",
 			"any(t.weight_unit) as weight_unit",

+ 74 - 0
graphql/query_product.go

@@ -124,6 +124,80 @@ func (r *productResolver) Variants(ctx context.Context, product *generated.Produ
 	return helper.NewProductVariantConnection(variants, after, before, first, last, reverse, sort)
 }
 
+func (r *productResolver) VariantBySelectedOptions(ctx context.Context, product *generated.Product, input []*generated.SelectedOptionInput) (*generated.ProductVariant, error) {
+	options, err := r.Options(ctx, product, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	size := len(options)
+	if size != len(input) {
+		return nil, fmt.Errorf("illegal SelectedOptionInput")
+	}
+
+	type ext struct {
+		id    string
+		index int
+	}
+	var selected = make([]ext, 0)
+
+	for i := range input {
+		for k := range options {
+			if options[k].Name != input[i].Name {
+				continue
+			}
+
+			for j, value := range options[k].Values {
+				if value != input[i].Value {
+					continue
+				}
+
+				selected = append(selected, ext{
+					id:    options[k].ID,
+					index: j + 1,
+				})
+				break
+			}
+		}
+	}
+
+	if size != len(selected) {
+		return nil, fmt.Errorf("illegal SelectedOptionInput")
+	}
+
+	variants, err := r.Variants(ctx, product, nil, nil, nil, nil, nil, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	for _, node := range variants.Nodes {
+		var i = 0
+
+		for _, e := range selected {
+			for _, meta := range node.Metafields {
+				if meta.Namespace != model.GidOption.String() {
+					continue
+				}
+
+				if t := model.MetaFieldType(meta.Type); !t.IsValid() || model.MetaFieldTypeNumberInteger != t {
+					continue
+				}
+
+				if e.id == meta.Key && fmt.Sprintf("%d", e.index) == meta.Value {
+					i++
+					break
+				}
+			}
+		}
+
+		if i == size {
+			return node, nil
+		}
+	}
+
+	return nil, nil
+}
+
 func (r *productVariantResolver) Product(ctx context.Context, variant *generated.ProductVariant) (*generated.Product, error) {
 	var (
 		inContext *middleware.GShopifyContext

+ 1 - 1
graphql/schema.graphql

@@ -286,7 +286,7 @@ type Product implements HasMetafields&Node&OnlineStorePublishable {
     # Find a product’s variant based on its selected options.
     # This is useful for converting a user’s selection of product options into a single matching variant.
     # If there is not a variant for the selected options, `null` will be returned.
-    variantBySelectedOptions(selectedOptions: [SelectedOptionInput!]!): ProductVariant
+    variantBySelectedOptions(selectedOptions: [SelectedOptionInput!]!): ProductVariant @goField(forceResolver: true)
 
     # List of the product’s variants.
     variants(

+ 6 - 7
relation/product.go

@@ -51,13 +51,12 @@ func (p *Product) As() *generated.Product {
 		PriceRange:          helper.NewPriceRange(p.PriceMin, p.PriceMax, generated.CurrencyCodeUsd),
 		CompareAtPriceRange: helper.NewPriceRange(p.CompareAtPriceMin, p.CompareAtPriceMax, generated.CurrencyCodeUsd),
 
-		RequiresSellingPlan:      false, // TODO:
-		FeaturedImage:            nil,   // TODO:
-		Images:                   nil,   // TODO:
-		Media:                    nil,   // TODO:
-		OnlineStoreURL:           nil,   // TODO:
-		SellingPlanGroups:        nil,   // TODO:
-		VariantBySelectedOptions: nil,   // TODO:
+		RequiresSellingPlan: false, // TODO:
+		FeaturedImage:       nil,   // TODO:
+		Images:              nil,   // TODO:
+		Media:               nil,   // TODO:
+		OnlineStoreURL:      nil,   // TODO:
+		SellingPlanGroups:   nil,   // TODO:
 
 		ID:              model.NewId(model.GidProduct, p.Id),
 		Handle:          p.Handle.String,

+ 14 - 0
relation/product_variant.go

@@ -1,6 +1,7 @@
 package relation
 
 import (
+	"fmt"
 	"github.com/gshopify/service-wrapper/model"
 	"github.com/mailru/dbr"
 	"gshopper.com/gshopify/products/graphql/generated"
@@ -31,6 +32,10 @@ type ProductVariant struct {
 	RequiresShipping    bool            `db:"requires_shipping"`
 	QuantityAvailable   int             `db:"available"`
 	ForSale             bool            `db:"for_sale"`
+	Options             []struct {
+		Field0 string
+		Field1 int32
+	} `db:"options"`
 }
 
 func (v *ProductVariant) As() *generated.ProductVariant {
@@ -58,6 +63,15 @@ func (v *ProductVariant) As() *generated.ProductVariant {
 		Value:     model.MetaFieldValue(model.MetaFieldTypeNumberInteger, v.Position),
 	})
 
+	for i := range v.Options {
+		variant.Metafields = append(variant.Metafields, &generated.Metafield{
+			Namespace: model.GidOption.String(),
+			Key:       model.NewId(model.GidOption, v.Options[i].Field0),
+			Type:      model.MetaFieldTypeNumberInteger.String(),
+			Value:     fmt.Sprintf("%d", v.Options[i].Field1),
+		})
+	}
+
 	if v.Weight.Valid {
 		s := v.Weight.Float64
 		variant.Weight = &s