Rails GraphQL中为Type类添加非模型自定义字段的实现问题
问题描述
我定义的PaymentType GraphQL类型比对应的Payment ActiveRecord模型多了一个xyz字段,希望在CreatePayment突变的resolve方法中获取xyz的数据,返回包含该字段的PaymentType实例。尝试过直接给payment变量添加属性、给PaymentType编写构造函数,均出现错误。
可行解决方案
方案1:给Payment模型添加虚拟属性
通过attr_accessor给模型添加虚拟属性,让ActiveRecord实例支持读写xyz字段,GraphQL会自动识别该属性:
修改Payment模型:
class Payment < ActiveRecord::Base field :id, Integer, null: false field :amount, Integer, null: false field :currency, String, null: false # 添加虚拟属性,支持xyz的读写 attr_accessor :xyz end
修改CreatePayment的resolve方法,给payment赋值xyz:
module Mutations class CreatePayment < BaseMutation type Types::PaymentType description "Create Payment" argument :amount, Integer, required: true argument :currency, String, required: true def resolve(amount:, currency:) payment = Payment.new(amount: amount, currency: currency) xyz = SomeXYZ() # 你的业务逻辑获取xyz值 payment.xyz = xyz payment.save payment end end end
PaymentType保持原有定义即可:
module Types class PaymentType < Types::BaseObject field :id, Integer, null: false field :amount, Integer, null: false field :currency, String, null: false field :xyz, Integer, null: true end end
方案2:在PaymentType的xyz字段中单独定义resolve逻辑
无需修改模型,直接在GraphQL字段中指定resolve逻辑,从返回的对象中读取xyz值:
修改CreatePayment的resolve方法,返回包含payment和xyz的结构体:
module Mutations class CreatePayment < BaseMutation type Types::PaymentType description "Create Payment" argument :amount, Integer, required: true argument :currency, String, required: true def resolve(amount:, currency:) payment = Payment.new(amount: amount, currency: currency) xyz = SomeXYZ() payment.save # 用OpenStruct包装payment数据和xyz OpenStruct.new(payment.attributes.merge(xyz: xyz)) end end end
修改PaymentType的xyz字段,添加自定义resolve:
module Types class PaymentType < Types::BaseObject field :id, Integer, null: false field :amount, Integer, null: false field :currency, String, null: false field :xyz, Integer, null: true do resolve ->(obj, args, ctx) { obj.xyz } end end end
方案3:自定义结构体包装数据
如果不想修改模型或使用OpenStruct,可以自定义结构体来封装payment和xyz:
先定义结构体(可放在app/graphql/types/目录下):
PaymentWithXyz = Struct.new(:payment, :xyz) do # 委托基础字段到payment实例 delegate :id, :amount, :currency, to: :payment end
修改CreatePayment的resolve方法返回结构体实例:
module Mutations class CreatePayment < BaseMutation type Types::PaymentType description "Create Payment" argument :amount, Integer, required: true argument :currency, String, required: true def resolve(amount:, currency:) payment = Payment.new(amount: amount, currency: currency) xyz = SomeXYZ() payment.save PaymentWithXyz.new(payment, xyz) end end end
修改PaymentType的字段resolve逻辑:
module Types class PaymentType < Types::BaseObject field :id, Integer, null: false, resolver: ->(obj, _, _) { obj.id } field :amount, Integer, null: false, resolver: ->(obj, _, _) { obj.amount } field :currency, String, null: false, resolver: ->(obj, _, _) { obj.currency } field :xyz, Integer, null: true, resolver: ->(obj, _, _) { obj.xyz } end end
错误原因说明
- 直接给payment加属性:ActiveRecord实例默认不允许读写未定义的属性,必须通过
attr_accessor声明虚拟属性。 - 给PaymentType写构造函数:GraphQL-Ruby的
BaseObject由框架自动初始化解析,自定义构造函数会破坏默认逻辑,导致初始化失败。
内容的提问来源于stack exchange,提问作者anshul mittal
相关产品推荐
相关产品推荐

