Skip to content

Commit c2e3901

Browse files
committed
Add ".where" functionality to graphql models
1 parent 0a75e6b commit c2e3901

File tree

9 files changed

+735
-32
lines changed

9 files changed

+735
-32
lines changed

lib/active_shopify_graphql.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# frozen_string_literal: true
22

3+
require 'active_support'
4+
require 'active_support/inflector'
5+
require 'active_support/concern'
6+
require 'active_support/core_ext/object/blank'
7+
require 'active_model'
8+
require 'globalid'
9+
310
require_relative "active_shopify_graphql/version"
411
require_relative "active_shopify_graphql/configuration"
512
require_relative "active_shopify_graphql/base"

lib/active_shopify_graphql/connections.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def metafield(attribute_name, graphql_field: "metafield", target_class: "Shopify
4949
end
5050
end
5151

52-
def connection(name, target_class: nil, arguments: {}, &block)
52+
def connection(name, target_class: nil, arguments: {})
5353
target_class_name = target_class&.to_s || name.to_s.singularize.classify
5454

5555
# Store connection metadata

lib/active_shopify_graphql/customer_account_api_loader.rb

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,38 @@ def initialize(token)
77
end
88

99
# Override to handle Customer queries that don't need an ID
10-
def graphql_query(model_type = 'Customer')
11-
if model_type == 'Customer'
10+
def graphql_query(model_type = nil)
11+
type = model_type || self.class.graphql_type
12+
if type == 'Customer'
1213
# Customer Account API doesn't need ID for customer queries - token identifies the customer
13-
customer_only_query(model_type)
14+
customer_only_query(type)
1415
else
1516
# For other types, use the standard query with ID
16-
super(model_type)
17+
super(type)
1718
end
1819
end
1920

2021
# Override load_attributes to handle the Customer case
21-
def load_attributes(id = nil, model_type = 'Customer')
22-
query = graphql_query(model_type)
22+
def load_attributes(model_type_or_id = nil, id = nil)
23+
# Handle both old and new signatures like the parent class
24+
if id.nil? && model_type_or_id.is_a?(String) && model_type_or_id != self.class.graphql_type
25+
# Old signature: load_attributes(model_type)
26+
type = model_type_or_id
27+
actual_id = nil
28+
elsif id.nil?
29+
# New signature: load_attributes() or load_attributes(id) - but for Customer, we don't need ID
30+
type = self.class.graphql_type
31+
actual_id = model_type_or_id
32+
else
33+
# Old signature: load_attributes(model_type, id)
34+
type = model_type_or_id
35+
actual_id = id
36+
end
37+
38+
query = graphql_query(type)
2339

2440
# For Customer queries, we don't need variables; for others, we need the ID
25-
variables = model_type == 'Customer' ? {} : { id: id }
41+
variables = type == 'Customer' ? {} : { id: actual_id }
2642

2743
response_data = execute_graphql_query(query, **variables)
2844

@@ -46,9 +62,10 @@ def execute_graphql_query(query, **variables)
4662
end
4763

4864
# Builds a customer-only query (no ID parameter needed)
49-
def customer_only_query(model_type)
50-
query_name_value = query_name(model_type)
51-
fragment_name_value = fragment_name(model_type)
65+
def customer_only_query(model_type = nil)
66+
type = model_type || self.class.graphql_type
67+
query_name_value = query_name(type)
68+
fragment_name_value = fragment_name(type)
5269

5370
<<~GRAPHQL
5471
#{fragment}

lib/active_shopify_graphql/finder_methods.rb

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module FinderMethods
1212
def find(id, loader: default_loader)
1313
gid = URI::GID.build(app: "shopify", model_name: model_name.name.demodulize, model_id: id)
1414
model_type = name.demodulize
15-
attributes = loader.load_attributes(gid, model_type)
15+
attributes = loader.load_attributes(model_type, gid)
1616

1717
return nil if attributes.nil?
1818

@@ -35,6 +35,48 @@ def default_loader=(loader)
3535
@default_loader = loader
3636
end
3737

38+
# Query for multiple records using attribute conditions
39+
# @param conditions [Hash] The conditions to query (e.g., { email: "example@test.com", first_name: "John" })
40+
# @param options [Hash] Options hash containing loader and limit (when first arg is a Hash)
41+
# @option options [ActiveShopifyGraphQL::Loader] :loader The loader to use for fetching data
42+
# @option options [Integer] :limit The maximum number of records to return (default: 250, max: 250)
43+
# @return [Array<Object>] Array of model instances
44+
# @raise [ArgumentError] If any attribute is not valid for querying
45+
#
46+
# @example
47+
# # Keyword argument style (recommended)
48+
# Customer.where(email: "john@example.com")
49+
# Customer.where(first_name: "John", country: "Canada")
50+
# Customer.where(orders_count: { gte: 5 })
51+
# Customer.where(created_at: { gte: "2024-01-01", lt: "2024-02-01" })
52+
#
53+
# # Hash style with options
54+
# Customer.where({ email: "john@example.com" }, loader: custom_loader, limit: 100)
55+
def where(conditions_or_first_condition = {}, *args, **options)
56+
# Handle both syntaxes:
57+
# where(email: "john@example.com") - keyword args become options
58+
# where({ email: "john@example.com" }, loader: custom_loader) - explicit hash + options
59+
if conditions_or_first_condition.is_a?(Hash) && !conditions_or_first_condition.empty?
60+
# Explicit hash provided as first argument
61+
conditions = conditions_or_first_condition
62+
# Any additional options passed as keyword args or second hash argument
63+
final_options = args.first.is_a?(Hash) ? options.merge(args.first) : options
64+
else
65+
# Keyword arguments style - conditions come from options, excluding known option keys
66+
known_option_keys = %i[loader limit]
67+
conditions = options.except(*known_option_keys)
68+
final_options = options.slice(*known_option_keys)
69+
end
70+
71+
loader = final_options[:loader] || default_loader
72+
limit = final_options[:limit] || 250
73+
74+
model_type = name.demodulize
75+
attributes_array = loader.load_collection(model_type, conditions, limit: limit)
76+
77+
attributes_array.map { |attributes| new(attributes) }
78+
end
79+
3880
private
3981

4082
# Infers the loader class name from the model name

0 commit comments

Comments
 (0)