[英]GraphQL Ruby: How to write DRY mutations with multiple parameters?
我正在使用 app 为我的graphql-ruby编写一个基于devise- jwt的身份验证系统。 在这个过程中,我为创建一个新的用户帐户做了一个改变,它需要 7 个参数,这在我的代码中产生了很多重复:
module Mutations
class SignUpMutation < Mutations::BaseMutation
argument :email, String, required: true
argument :password, String, required: true
argument :family_name, String, required: true
argument :family_name_phonetic, String, required: true
argument :given_name, String, required: true
argument :given_name_phonetic, String, required: true
argument :newsletter_optin, Boolean, required: false
field :token, String, null: true
field :user, Types::UserType, null: true
def resolve(email:, password:,
family_name:, family_name_phonetic:,
given_name:, given_name_phonetic:,
newsletter_optin:
)
result = {
token: nil,
user: nil
}
new_user = User.new(
email: email,
password: password,
family_name: family_name,
family_name_phonetic: family_name_phonetic,
given_name: given_name,
given_name_phonetic: given_name_phonetic,
newsletter_optin: newsletter_optin
)
if new_user.save!
result[:token] = new_user.token
result[:user] = new_user
end
result
end
end
end
我怎么能干掉这个以避免在所有地方重复突变参数的名称?
先感谢您!
回答我自己的问题。 不必处理这么多参数的正确方法是使用输入对象而不是单独的参数。 来自graphql-ruby 文档:
输入对象类型是 GraphQL 操作的复杂输入。 它们非常适合需要大量结构化输入的字段,例如突变或搜索字段。
所以我已经定义了我的输入对象:
module Types
class UserAttributes < Types::BaseInputObject
description 'Attributes for creating or updating a user'
argument :email, String, required: true
argument :password, String, required: true
argument :family_name, String, required: true
argument :family_name_phonetic, String, required: true
argument :given_name, String, required: true
argument :given_name_phonetic, String, required: true
argument :newsletter_optin, Boolean, required: false
end
end
然后像这样重构我的突变:
module Mutations
class SignUpMutation < Mutations::BaseMutation
argument :attributes, Types::UserAttributes, required: true
field :token, String, null: true
field :user, Types::UserType, null: true
def resolve(attributes:)
result = {
token: nil,
user: nil
}
new_user = User.new(attributes.to_hash)
if new_user.save!
result[:token] = new_user.token
result[:user] = new_user
end
result
end
end
end
最后,这段代码感觉更像 ruby :)
如果你愿意,你可以做这样的事情:
[
:email,
:password,
:family_name,
:family_name_phonetic,
:given_name,
:given_name_phonetic
].each do |arg|
argument arg, String, required: true
end
您可能认为除此之外再多也不为过,但 Ruby非常灵活。 如果你真的想,你甚至可以做类似的事情
def resolve(email:, password:,
family_name:, family_name_phonetic:,
given_name:, given_name_phonetic:,
newsletter_optin:)
result = {
token: nil,
user: nil
}
params = method(__method__).parameters.map(&:last)
opts = params.map{|p| [p, eval(p.to_s)]}.to_h
new_user = User.new(opts)
if new_user.save!
result[:token] = new_user.token
result[:user] = new_user
end
result
end
您可以查看此答案以获取解释
如果您想要更多,您可以使用更详细的字段列表和define_method
- 您可以一直得到它,直到您只输入一次,例如:email
。
那会更好吗? 也许,如果你有数百个这样的事情要做。 或者,如果您想在运行时开始定义事物。
您可以尝试使用 double splat (**) 运算符。
module Mutations
class SignUpMutation < Mutations::BaseMutation
argument :email, String, required: true
argument :password, String, required: true
argument :family_name, String, required: true
argument :family_name_phonetic, String, required: true
argument :given_name, String, required: true
argument :given_name_phonetic, String, required: true
argument :newsletter_optin, Boolean, required: false
field :token, String, null: true
field :user, Types::UserType, null: true
def resolve(**arguments)
result = {
token: nil,
user: nil
}
new_user = User.new(
email: arguments[:email],
password: arguments[:password],
family_name: arguments[:family_name],
family_name_phonetic: arguments[:family_name_phonetic],
given_name: arguments[:given_name],
given_name_phonetic: arguments[:given_name_phonetic],
newsletter_optin: arguments[:newsletter_optin]
)
if new_user.save!
result[:token] = new_user.token
result[:user] = new_user
end
result
end
end
end
当然,像您一样创建一个新类型会更整洁。 但在某些情况下,您可以将它们组合在一起,例如
module Mutations
class SignUpMutation < Mutations::BaseMutation
argument :another_attribute, String, required: true
argument :attributes, Types::UserAttributes, required: true
field :token, String, null: true
field :user, Types::UserType, null: true
def resolve(**arguments)
result = {
token: nil,
user: nil
}
# use arguments[:another_attribute] for something else.
new_user = User.new(arguments[:attributes].to_hash)
if new_user.save!
result[:token] = new_user.token
result[:user] = new_user
end
result
end
end
end
在您的情况下,我也会使用输入对象,但是如果您有一个现有 API 且客户端依赖于模式,并且您想“干燥”那些在不同突变/字段中完全相同的重复参数,您会怎么做?
如果您继续实现一个新的输入对象,您将更改架构并且客户端很可能会中断。 我想在将现有参数移动到输入对象中时没有办法保持模式相同,对吗?
在不干扰现有 GraphQL 模式的情况下,更好的方法是使用所有常见参数定义 InputType,例如:
module Types
module Inputs
class CommonInputType < Types::Root::BaseInputObject
graphql_name("my_common_input_type")
argument :email, String, required: true
argument :newsletter_optin, Boolean, required: true
...
argument :posts, [Types::Inputs::Post], required: true
end
end
end
&在一些带有附加参数的突变中使用它,例如:
module Mutations
class CreateUser < Mutations::BaseMutation
argument :additional_arg_one, ID, required: true
argument :additional_arg_two, String, required: false
...
Types::Inputs::CommonInputType.arguments.each do |arg,properties|
argument arg.to_sym, properties.graphql_definition.type
end
end
end
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.