Представление отношения has_many в Rails с абстрактным классом - PullRequest
0 голосов
/ 31 мая 2018

У меня есть отношение, которое мне трудно моделировать.

У меня есть класс Subscription, который является обычной моделью ActiveRecord, и он может иметь один или несколько PaymentSources.Проблема, однако, заключается в том, что источником платежа может быть либо CreditCard, либо BankAccount.

Учитывая, что с этими моделями связаны очень разные данные, я не думаю, что STI - хороший вариант здесь.Поэтому мне было интересно, есть ли установленный или рекомендуемый подход для ситуации в Rails, где модель имеет_множество другой модели, которая на самом деле является абстракцией для 2 или более классов, которые не используют один и тот же макет данных.

В идеале, в этом конкретном примере я мог бы сказать что-то вроде subscription.payment_source.default и сделать так, чтобы оно ссылалось либо на CreditCard, либо на BankAccount в зависимости от того, что пользователь выбрал в качестве предпочтительного метода выставления счетов.

1 Ответ

0 голосов
/ 31 мая 2018

TLDR:

[Обновлено] После некоторых размышлений я сделаю Вариант 2 (более полное решение), который будет гибким на будущее, но если вам не понадобится вся эта сложность, я 'Я сделаю только Вариант 1.

Вариант 1:

class Subscription < ApplicationRecord
  belongs_to :credit_card
  belongs_to :bank_account

  def payment_sources
    [credit_card, bank_account].compact
  end

  def default_payment_source
    case user.preferred_billing_method # assuming you have an integer column in users table called `preferred_billing_method`
    when 0 then credit_card # asssuming 0 means "Credit Card"
    when 1 then bank_account # assuming 1 means "Bank Account"
    else NotImplementedError
    end
  end
end

Использование

Subscription.first.default_payment_source
# => returns either `CreditCard` or `BankAccount`, or `nil`

Subscription.first.payment_sources.first
# => returns either `CreditCard` or `BankAccount`, or `nil`

Вариант 2:

class User < ApplicationRecord
  belongs_to :default_payment_source, class_name: 'PaymentSource'
  has_many :subscriptions
end

class Subscription < ApplicationRecord
  belongs_to :user
  has_many :payment_sources_subscriptions
  has_many :payment_sources, through: :payment_sources_subscriptions
end

# This is just a join-model
class PaymentSourcesSubscription < ApplicationRecord
  belongs_to :subscription
  belongs_to :payment_source

  validates :subscription, uniqueness: { scope: :payment_source }
end

# this is your "abstract" model for "payment sources"
class PaymentSource < ApplicationRecord
  belongs_to :payment_sourceable, polymorphic: true
  has_many :payment_sources_subscriptions
  has_many :subscriptions, through: :payment_sources_subscriptions

  validates :payment_sourceable, uniqueness: true
end

class CreditCard < ApplicationRecord
  has_one :payment_source, as: :payment_sourceable
end

class BankAccount < ApplicationRecord
  has_one :payment_source, as: :payment_sourceable
end

Использование:

User.first.default_payment_source.payment_sourceable
# => returns either `CreditCard` or `BankAccount`, or `nil`

Subscription.first.payment_sources.first.payment_sourceable
# => returns either `CreditCard` or `BankAccount`, or `nil`
...