0
\$\begingroup\$

I'm trying to manage several means of payment in my Rails 3.2 application.

Let's say I have 2 means : Credit Cards & Bank Transfers (BIC), working with 2 different payment providers. Those means both contain a reference string that will be sent to the payment provider to trigger new payments later. Each means also contains specific information. This is why I'm thinking about creating 2 models (a CreditCard one and a Bic one). And I would like to link both models to the User model.

A single user can have for example 2 credit cards and 1 BIC so there must be at least a have_many association. I don't want my user model to have too many different associations, but I want it to have the same behavior for each kind of payment mean. I though about using the Single Table Inheritance but I previously had a bad experience with the combo "STI + associations" and I lost a lot of time.

So, my solution to that problem would be to make an intermediate model PaymentMean that would:

  1. belong to User

  2. be associated to both CreditCard and Bic with a polymorphic "belongs_to" association

as follows:

class User < ActiveRecord::Base
  has_many :payment_means
end

class PaymentMean < ActiveRecord::Base
  belongs_to :user
  belongs_to :payment, polymorphic: true
end

class CreditCard < ActiveRecord::Base
  has_one :payment_mean, as: :payment
end

class Bic < ActiveRecord::Base
  has_one :payment_mean, as: :payment
end

What do you think about it ? Should I try to use STI (if so, HOW ?) or is this model viable ? Or is there any other solution ?

\$\endgroup\$
1
\$\begingroup\$

First: "Means" is uncountable in English; it's always plural. So the model should be called PaymentMeans, and associations should be has_many :payment_means and has_one :payment_means. You can set that up in Rails' inflections config (or just call it "payment type" or something).

Anyway, I would definitely consider STI for this, depending on how similar the models really are. It really all depends on that. The more they have in common, the better suited an STI solution will be. As far as I can tell, there shouldn't be any major pitfalls. However, you've only said they "contain specific information" but without knowing what exactly that entails, it's hard to know if it'd overburden an STI approach.

Alternatively, if they have little in common beyond the reference string, your current solution seems fine, but it does add an extra association and thus extra complexity.

If you're adamant about avoiding STI, I'd think you can skip the PaymentMean (sic) model, and simply make the polymorphic association directly to the User model:

class User
  belongs_to :payment_means, polymorphic: true # can be CreditCard or Bic
end

You can then extract a service object to handle the things you'd currently handle with the PaymentMean model (in fact, you could consider this, regardless of how your models are otherwise set up).

Of course it seems semantically awkward to say that a "User belongs to their Payment Means" (although that's how banks probably think), so once again your current solution or STI might be better suited.

Lastly, you could reduce your payment means model to just the bare necessities, and keep everything else in a serialized attribute (i.e. a hash). Of course this would likely entail writing a custom validator or similar busywork, but it would be very flexible. It still is a "last resort" solution, though.

\$\endgroup\$
  • \$\begingroup\$ Thank you Flambino for you anwser! First, about the english grammar, we use to pay attention on it but we actually are a french team so we often make this kind of mistakes. \$\endgroup\$ – Jejekeny Apr 15 '14 at 16:14
  • \$\begingroup\$ The solution with STI would actually be more complex because we need the user to "have many" payment types. He could for example have 2 credit cards and 1 BIC. To me, it means that we would have to make a "payment" class, associated to user. Then, make two classes (cc and bic) inherited from "payment". That's why we choose the current solution and wondered if it is viable or not. \$\endgroup\$ – Jejekeny Apr 15 '14 at 16:20
  • \$\begingroup\$ @Jejekeny Ah, I thought there'd be only one form of payment per user. Anyway, I'd say your solution is fine. Again: It really depends on how much a Bic and a CreditCard record have in common \$\endgroup\$ – Flambino Apr 15 '14 at 18:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.